<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.3.0">


  <link rel="apple-touch-icon" sizes="180x180" href="/yuwanzi.io/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/yuwanzi.io/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/yuwanzi.io/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/yuwanzi.io/images/logo.svg" color="#222">

<link rel="stylesheet" href="/yuwanzi.io/css/main.css">



<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.1/css/all.min.css">
  <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/animate.css@3.1.1/animate.min.css">

<script class="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"suyuhuan.gitee.io","root":"/yuwanzi.io/","images":"/yuwanzi.io/images","scheme":"Muse","version":"8.2.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":false,"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"Suche...","empty":"We didn't find any results for the search: ${query}","hits_time":"${hits} results found in ${time} ms","hits":"${hits} results found"}};
  </script>
<meta name="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
<meta property="og:type" content="website">
<meta property="og:title" content="玉丸子 | Blog">
<meta property="og:url" content="https://suyuhuan.gitee.io/yuwanzi.io/page/5/index.html">
<meta property="og:site_name" content="玉丸子 | Blog">
<meta property="og:description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
<meta property="og:locale">
<meta property="article:author" content="玉丸子">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="https://suyuhuan.gitee.io/yuwanzi.io/page/5/">


<script class="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : true,
    isPost : false,
    lang   : 'zh'
  };
</script>
<title>玉丸子 | Blog</title>
  




  <noscript>
  <style>
  body { margin-top: 2rem; }

  .use-motion .menu-item,
  .use-motion .sidebar,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header {
    visibility: visible;
  }

  .use-motion .header,
  .use-motion .site-brand-container .toggle,
  .use-motion .footer { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle,
  .use-motion .custom-logo-image {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line {
    transform: scaleX(1);
  }

  .search-pop-overlay, .sidebar-nav { display: none; }
  .sidebar-panel { display: block; }
  </style>
</noscript>

<link rel="alternate" href="/yuwanzi.io/atom.xml" title="玉丸子 | Blog" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="Navigationsleiste an/ausschalten" role="button">
    </div>
  </div>

  <div class="site-meta">

    <a href="/yuwanzi.io/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">玉丸子 | Blog</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>







</div>
        
  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-overview-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          Inhaltsverzeichnis
        </li>
        <li class="sidebar-nav-overview">
          Übersicht
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">玉丸子</p>
  <div class="site-description" itemprop="description">这里是玉丸子的个人博客,与你一起发现更大的世界。</div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/yuwanzi.io/archives">
          <span class="site-state-item-count">68</span>
          <span class="site-state-item-name">Artikel</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/yuwanzi.io/categories/">
        <span class="site-state-item-count">39</span>
        <span class="site-state-item-name">Kategorien</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/yuwanzi.io/tags/">
        <span class="site-state-item-count">46</span>
        <span class="site-state-item-name">schlagwörter</span></a>
      </div>
  </nav>
</div>



        </div>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top" role="button">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner index posts-expand">

    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2017/01/12/2017-01-12-SimpleHashMap/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2017/01/12/2017-01-12-SimpleHashMap/" class="post-title-link" itemprop="url">实现一个简单的HashMap</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2017-01-12 18:00:00" itemprop="dateCreated datePublished" datetime="2017-01-12T18:00:00+08:00">2017-01-12</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/" itemprop="url" rel="index"><span itemprop="name">Algorithms</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" itemprop="url" rel="index"><span itemprop="name">数据结构</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/HashTable/" itemprop="url" rel="index"><span itemprop="name">HashTable</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><hr>
<p>&nbsp;&nbsp;HashMap是基于哈希表的Map接口的实现,以key-value的形式存在,在HashMap中,key-value会被当成一个整体(Entry)来处理,HashMap内部维护了一个链表数组,会根据hash算法来计算key-value在数组中的存储位置.</p>
<h3 id="HashMap的内部结构"><a href="#HashMap的内部结构" class="headerlink" title="HashMap的内部结构"></a>HashMap的内部结构</h3><hr>
<p><img src="http://wx3.sinaimg.cn/mw690/63503acbly1fbo5dcppj6j20hs0hkwmv.jpg"></p>
<p>&nbsp;&nbsp;HashMap内部使用了桶(bucket)来存储键值对,桶就是一个存储key-value的链表.而HashMap中维护了一个数组,这个数组的每个元素就是一个桶(bucket),在HashMap中,桶是使用链表实现的.</p>
<ul>
<li><p> HashMap使用散列函数(hash)将给定键转化为一个数组索引(桶号),不同的key会被转化为不同的索引,实际中有几率会把不同的键转化为同一个索引,这种情况叫做hash碰撞,HashMap使用了拉链法解决hash碰撞.</p>
</li>
<li><p>当发生hash碰撞时,会生成一个新的key-value对象,并将新对象挂在链表头部.</p>
</li>
<li><p> 得到数组索引后,就可以遍历这个链表来进行各种操作了.</p>
</li>
</ul>
<p>&nbsp;&nbsp;在HashMap中有2个重要的参数:capaCity(容量),loadFactor(负载因子).</p>
<ul>
<li><p>容量:它表示HashMap中桶的数量.</p>
</li>
<li><p>负载因子:它是HashMap在其容量自动扩容之前可以达到多满的一种尺度,它衡量的是一个散列表的空间使用程度,负载因子越大表示散列表的空间利用率越大,反之越小.对于使用拉链法的散列表来说,查找一个元素的平均时间是O(1+a),因此负载因子越大,对空间的利用越充分,但是查找效率就会越低,如果负载因子很小,那么散列表的空间利用率将会很小,对空间造成严重浪费,但是查找效率则会变快.HashMap中负载因子的默认值为0.75.</p>
</li>
<li><p>阈值:容量自动扩展的阈值,当HashMap中的键值对数量到达阈值时,将会进行自动扩容(当前容量x2),阈值通常的计算方法为 capaCity * loadFactor.</p>
</li>
</ul>
<h3 id="简单实现"><a href="#简单实现" class="headerlink" title="简单实现"></a>简单实现</h3><hr>
<h4 id="construction"><a href="#construction" class="headerlink" title="construction"></a>construction</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 构造一个空的SimpleHashMap,使用默认的capacity和负载因子</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">SimpleHashMap</span><span class="params">(<span class="keyword">int</span> initialiCapacity, <span class="keyword">float</span> loadFactor)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (initialiCapacity &lt; <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;Illegal initial capacity: &quot;</span></span><br><span class="line">                + initialiCapacity);</span><br><span class="line">    <span class="keyword">if</span> (initialiCapacity &gt; MAXIMUM_CAPACITY)</span><br><span class="line">        initialiCapacity = MAXIMUM_CAPACITY;</span><br><span class="line">    <span class="keyword">if</span> (loadFactor &lt;= <span class="number">0</span> || Float.isNaN(loadFactor))</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;Illegal load factor: &quot;</span></span><br><span class="line">                + loadFactor);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//计算出大于initialCapacity的最小的2的n次方值</span></span><br><span class="line">    <span class="keyword">int</span> capacity = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span> (capacity &lt; initialiCapacity) &#123;</span><br><span class="line">        capacity &lt;&lt;= <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">this</span>.loadFactor = loadFactor;</span><br><span class="line">    <span class="comment">//设置HashMap的扩容阈值,当到达这个阈值时会进行自动扩容</span></span><br><span class="line">    threshold = (<span class="keyword">int</span>) (capacity * loadFactor);</span><br><span class="line">    <span class="comment">//初始化table数组</span></span><br><span class="line">    table = <span class="keyword">new</span> Node[capacity];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;其中table是一个Node&lt;K,V&gt;数组,它是由链表实现的.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt; <span class="keyword">implements</span> <span class="title">Map</span>.<span class="title">Entry</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt; </span>&#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> hash;</span><br><span class="line">        <span class="keyword">final</span> K key;</span><br><span class="line">        V value;</span><br><span class="line">        Node&lt;K, V&gt; next;</span><br><span class="line"></span><br><span class="line">        Node(<span class="keyword">int</span> hash, K key, V value, Node&lt;K, V&gt; next) &#123;</span><br><span class="line">            <span class="keyword">this</span>.hash = hash;</span><br><span class="line">            <span class="keyword">this</span>.key = key;</span><br><span class="line">            <span class="keyword">this</span>.value = value;</span><br><span class="line">            <span class="keyword">this</span>.next = next;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> K <span class="title">getKey</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> key;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> V <span class="title">getValue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> value;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> key + <span class="string">&quot;=&quot;</span> + value;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">hashCode</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> Objects.hashCode(key) ^ Objects.hashCode(value);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> V <span class="title">setValue</span><span class="params">(V newValue)</span> </span>&#123;</span><br><span class="line">            V oldValue = value;</span><br><span class="line">            value = newValue;</span><br><span class="line">            <span class="keyword">return</span> oldValue;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">equals</span><span class="params">(Object o)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (o == <span class="keyword">this</span>)</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            <span class="keyword">if</span> (o <span class="keyword">instanceof</span> Map.Entry) &#123;</span><br><span class="line">                Map.Entry&lt;?, ?&gt; e = (Entry&lt;?, ?&gt;) o;</span><br><span class="line">                <span class="keyword">if</span> (Objects.equals(key, e.getKey()) &amp;&amp;</span><br><span class="line">                        Objects.equals(value, e.getValue())) &#123;</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<h4 id="put"><a href="#put" class="headerlink" title="put"></a>put</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">put</span><span class="params">(K key, V value)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//如果key为null,调用putForNullKey()向null key存入value</span></span><br><span class="line">        <span class="keyword">if</span> (key == <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">return</span> putForNullKey(value);</span><br><span class="line">        <span class="comment">//计算key的hash</span></span><br><span class="line">        <span class="keyword">int</span> hash = hash(key.hashCode());   ---<span class="number">1</span></span><br><span class="line">        <span class="comment">//计算key的hash在table数组中的索引</span></span><br><span class="line">        <span class="keyword">int</span> i = indexFor(hash, table.length);  --<span class="number">2</span></span><br><span class="line">        <span class="comment">//遍历table</span></span><br><span class="line">        <span class="keyword">for</span> (Node&lt;K, V&gt; e = table[i]; e != <span class="keyword">null</span>; e = e.next) &#123;</span><br><span class="line">            Object k;</span><br><span class="line">            <span class="comment">//如果有相同的key,直接覆盖value,返回oldValue</span></span><br><span class="line">            <span class="keyword">if</span> (e.hash == hash &amp;&amp; ((k = e.key) == key || key.equals(k))) &#123;</span><br><span class="line">                V oldValue = e.value;</span><br><span class="line">                e.value = value;</span><br><span class="line">                <span class="keyword">return</span> oldValue;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        modCount++; <span class="comment">//修改次数++</span></span><br><span class="line">        <span class="comment">//将key,value添加至i处</span></span><br><span class="line">        addEntry(hash, key, value, i);  --<span class="number">3</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;看以上代码1、2处,这2个函数计算了hash值和bucket索引.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 预处理hash值，避免较差的离散hash序列，导致桶没有充分利用.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">hash</span><span class="params">(<span class="keyword">int</span> h)</span> </span>&#123;</span><br><span class="line">    h ^= (h &gt;&gt;&gt; <span class="number">20</span>) ^ (h &gt;&gt;&gt; <span class="number">12</span>);</span><br><span class="line">    <span class="keyword">return</span> h ^ (h &gt;&gt;&gt; <span class="number">7</span>) ^ (h &gt;&gt;&gt; <span class="number">4</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 返回对应hash值得索引</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">indexFor</span><span class="params">(<span class="keyword">int</span> h, <span class="keyword">int</span> length)</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 由于length是2的n次幂，所以h &amp; (length-1)相当于h % length。</span></span><br><span class="line"><span class="comment">     * 对于length，其2进制表示为1000...0，那么length-1为0111...1。</span></span><br><span class="line"><span class="comment">     * 那么对于任何小于length的数h，该式结果都是其本身h。</span></span><br><span class="line"><span class="comment">     * 对于h = length，该式结果等于0。</span></span><br><span class="line"><span class="comment">     * 对于大于length的数h，则和0111...1位与运算后，</span></span><br><span class="line"><span class="comment">     * 比0111...1高或者长度相同的位都变成0，</span></span><br><span class="line"><span class="comment">     * 相当于减去j个length，该式结果是h-j*length，</span></span><br><span class="line"><span class="comment">     * 所以相当于h % length。</span></span><br><span class="line"><span class="comment">     * 其中一个很常用的特例就是h &amp; 1相当于h % 2。</span></span><br><span class="line"><span class="comment">     * 这也是为什么length只能是2的n次幂的原因，为了优化。</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">return</span> h % (length - <span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;代码3的addEntry函数向数组添加了一对key-value.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 添加一对key-value,如果当前索引上已有桶(发生hash碰撞),则将新元素放入链表头</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">addEntry</span><span class="params">(<span class="keyword">int</span> hash, K key, V value, <span class="keyword">int</span> bucketIndex)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//保存对应table的值</span></span><br><span class="line">    Node&lt;K, V&gt; e = table[bucketIndex];</span><br><span class="line">    <span class="comment">//用新桶链住旧桶</span></span><br><span class="line">    table[bucketIndex] = <span class="keyword">new</span> Node&lt;K, V&gt;(hash, key, value, e);</span><br><span class="line">    <span class="comment">//如果HashMap中元素的个数已经超过阈值,则扩容两倍</span></span><br><span class="line">    <span class="keyword">if</span> (size++ &gt;= threshold)</span><br><span class="line">        resize(<span class="number">2</span> * table.length);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="get"><a href="#get" class="headerlink" title="get"></a>get</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">get</span><span class="params">(Object key)</span> </span>&#123;</span><br><span class="line">       <span class="comment">//如果key为null,则调用getForNullkey()获得key为null的值</span></span><br><span class="line">       <span class="keyword">if</span> (key == <span class="keyword">null</span>)</span><br><span class="line">           <span class="keyword">return</span> getForNullKey();</span><br><span class="line">       <span class="comment">//根据key的hashCode计算它的hash</span></span><br><span class="line">       <span class="keyword">int</span> hash = hash(key.hashCode());</span><br><span class="line">       <span class="comment">//取出table中指定索引处的值</span></span><br><span class="line">       <span class="keyword">for</span> (Node&lt;K, V&gt; e = table[indexFor(hash, table.length)]; e != <span class="keyword">null</span>; e = e.next) &#123;</span><br><span class="line">           Object k;</span><br><span class="line">           <span class="comment">//如果查找相同的key,返回其对应的value</span></span><br><span class="line">           <span class="keyword">if</span> (e.hash == hash &amp;&amp; ((k = e.key) == key || key.equals(k))) &#123;</span><br><span class="line">               <span class="keyword">return</span> e.value;</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>
<h4 id="all"><a href="#all" class="headerlink" title="all"></a>all</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 一个简单的HashMap,内部使用拉链法解决hash碰撞.</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * Created by sylvanasp on 2017/1/12.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SimpleHashMap</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt; <span class="keyword">extends</span> <span class="title">AbstractMap</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt;</span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">Map</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt;, <span class="title">Cloneable</span>, <span class="title">Serializable</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = <span class="number">6623475452522370065L</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 默认的容量(bucket数量),1 &lt;&lt; 4(16).</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DEFAULT_INITIAL_CAPACITY = <span class="number">1</span> &lt;&lt; <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 最大的容量,1 &lt;&lt; 30 (2^30)</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MAXIMUM_CAPACITY = <span class="number">1</span> &lt;&lt; <span class="number">30</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 默认的负载因子,0.75.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">float</span> DEFAULT_LOAD_FACTOR = <span class="number">0.75f</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * KV链表</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt; <span class="keyword">implements</span> <span class="title">Map</span>.<span class="title">Entry</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt; </span>&#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> hash;</span><br><span class="line">        <span class="keyword">final</span> K key;</span><br><span class="line">        V value;</span><br><span class="line">        Node&lt;K, V&gt; next;</span><br><span class="line"></span><br><span class="line">        Node(<span class="keyword">int</span> hash, K key, V value, Node&lt;K, V&gt; next) &#123;</span><br><span class="line">            <span class="keyword">this</span>.hash = hash;</span><br><span class="line">            <span class="keyword">this</span>.key = key;</span><br><span class="line">            <span class="keyword">this</span>.value = value;</span><br><span class="line">            <span class="keyword">this</span>.next = next;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> K <span class="title">getKey</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> key;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> V <span class="title">getValue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> value;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> key + <span class="string">&quot;=&quot;</span> + value;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">hashCode</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> Objects.hashCode(key) ^ Objects.hashCode(value);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> V <span class="title">setValue</span><span class="params">(V newValue)</span> </span>&#123;</span><br><span class="line">            V oldValue = value;</span><br><span class="line">            value = newValue;</span><br><span class="line">            <span class="keyword">return</span> oldValue;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">equals</span><span class="params">(Object o)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (o == <span class="keyword">this</span>)</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            <span class="keyword">if</span> (o <span class="keyword">instanceof</span> Map.Entry) &#123;</span><br><span class="line">                Map.Entry&lt;?, ?&gt; e = (Entry&lt;?, ?&gt;) o;</span><br><span class="line">                <span class="keyword">if</span> (Objects.equals(key, e.getKey()) &amp;&amp;</span><br><span class="line">                        Objects.equals(value, e.getValue())) &#123;</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 链表数组,数组中的每一个元素代表了一个链表的头部.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">transient</span> Node[] table;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 当前map的key-value映射数，也就是当前size</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">transient</span> <span class="keyword">int</span> size;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 代表这个HashMap修改key-value的次数.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">transient</span> <span class="keyword">int</span> modCount;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 自动扩展的阈值(capacity * loadfactor)</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">int</span> threshold;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 负载因子:</span></span><br><span class="line"><span class="comment">     * 它是哈希表在其容量自动增加之前可以达到多满的一种尺度，</span></span><br><span class="line"><span class="comment">     * 它衡量的是一个散列表的空间的使用程度，负载因子越大表示散列表的装填程度越高，</span></span><br><span class="line"><span class="comment">     * 反之愈小。对于使用链表法的散列表来说，查找一个元素的平均时间是O(1+a)，</span></span><br><span class="line"><span class="comment">     * 因此如果负载因子越大，对空间的利用更充分，然而后果是查找效率的降低；</span></span><br><span class="line"><span class="comment">     * 如果负载因子太小，那么散列表的数据将过于稀疏，对空间造成严重浪费.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">float</span> loadFactor;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 构造一个空的SimpleHashMap,使用默认的capacity和负载因子</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">SimpleHashMap</span><span class="params">(<span class="keyword">int</span> initialiCapacity, <span class="keyword">float</span> loadFactor)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (initialiCapacity &lt; <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;Illegal initial capacity: &quot;</span></span><br><span class="line">                    + initialiCapacity);</span><br><span class="line">        <span class="keyword">if</span> (initialiCapacity &gt; MAXIMUM_CAPACITY)</span><br><span class="line">            initialiCapacity = MAXIMUM_CAPACITY;</span><br><span class="line">        <span class="keyword">if</span> (loadFactor &lt;= <span class="number">0</span> || Float.isNaN(loadFactor))</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;Illegal load factor: &quot;</span></span><br><span class="line">                    + loadFactor);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//计算出大于initialCapacity的最小的2的n次方值</span></span><br><span class="line">        <span class="keyword">int</span> capacity = <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span> (capacity &lt; initialiCapacity) &#123;</span><br><span class="line">            capacity &lt;&lt;= <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">this</span>.loadFactor = loadFactor;</span><br><span class="line">        <span class="comment">//设置HashMap的扩容阈值,当到达这个阈值时会进行自动扩容</span></span><br><span class="line">        threshold = (<span class="keyword">int</span>) (capacity * loadFactor);</span><br><span class="line">        <span class="comment">//初始化table数组</span></span><br><span class="line">        table = <span class="keyword">new</span> Node[capacity];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">SimpleHashMap</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 预处理hash值，避免较差的离散hash序列，导致桶没有充分利用.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">hash</span><span class="params">(<span class="keyword">int</span> h)</span> </span>&#123;</span><br><span class="line">        h ^= (h &gt;&gt;&gt; <span class="number">20</span>) ^ (h &gt;&gt;&gt; <span class="number">12</span>);</span><br><span class="line">        <span class="keyword">return</span> h ^ (h &gt;&gt;&gt; <span class="number">7</span>) ^ (h &gt;&gt;&gt; <span class="number">4</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 返回对应hash值得索引</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">indexFor</span><span class="params">(<span class="keyword">int</span> h, <span class="keyword">int</span> length)</span> </span>&#123;</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 由于length是2的n次幂，所以h &amp; (length-1)相当于h % length。</span></span><br><span class="line"><span class="comment">         * 对于length，其2进制表示为1000...0，那么length-1为0111...1。</span></span><br><span class="line"><span class="comment">         * 那么对于任何小于length的数h，该式结果都是其本身h。</span></span><br><span class="line"><span class="comment">         * 对于h = length，该式结果等于0。</span></span><br><span class="line"><span class="comment">         * 对于大于length的数h，则和0111...1位与运算后，</span></span><br><span class="line"><span class="comment">         * 比0111...1高或者长度相同的位都变成0，</span></span><br><span class="line"><span class="comment">         * 相当于减去j个length，该式结果是h-j*length，</span></span><br><span class="line"><span class="comment">         * 所以相当于h % length。</span></span><br><span class="line"><span class="comment">         * 其中一个很常用的特例就是h &amp; 1相当于h % 2。</span></span><br><span class="line"><span class="comment">         * 这也是为什么length只能是2的n次幂的原因，为了优化。</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">return</span> h % (length - <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获得key为null的值</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> V <span class="title">getForNullKey</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">//遍历table[0]</span></span><br><span class="line">        <span class="keyword">for</span> (Node&lt;K, V&gt; e = table[<span class="number">0</span>]; e != <span class="keyword">null</span>; e = e.next) &#123;</span><br><span class="line">            <span class="comment">//如果找到key为null,则返回对应的值</span></span><br><span class="line">            <span class="keyword">if</span> (e.key == <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> e.value;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 当Key为Null时如何放入值</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> V <span class="title">putForNullKey</span><span class="params">(V value)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//遍历table[0]</span></span><br><span class="line">        <span class="keyword">for</span> (Node&lt;K, V&gt; e = table[<span class="number">0</span>]; e != <span class="keyword">null</span>; e = e.next) &#123;</span><br><span class="line">            <span class="keyword">if</span> (e.key == <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="comment">//取出oldValue,并存入newValue</span></span><br><span class="line">                V oldValue = e.value;</span><br><span class="line">                e.value = value;</span><br><span class="line">                <span class="comment">//返回oldValue</span></span><br><span class="line">                <span class="keyword">return</span> oldValue;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        modCount++;</span><br><span class="line">        addEntry(<span class="number">0</span>, <span class="keyword">null</span>, value, <span class="number">0</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 添加一对key-value,如果当前索引上已有桶(发生hash碰撞),则将新元素放入链表头</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">addEntry</span><span class="params">(<span class="keyword">int</span> hash, K key, V value, <span class="keyword">int</span> bucketIndex)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//保存对应table的值</span></span><br><span class="line">        Node&lt;K, V&gt; e = table[bucketIndex];</span><br><span class="line">        <span class="comment">//用新桶链住旧桶</span></span><br><span class="line">        table[bucketIndex] = <span class="keyword">new</span> Node&lt;K, V&gt;(hash, key, value, e);</span><br><span class="line">        <span class="comment">//如果HashMap中元素的个数已经超过阈值,则扩容两倍</span></span><br><span class="line">        <span class="keyword">if</span> (size++ &gt;= threshold)</span><br><span class="line">            resize(<span class="number">2</span> * table.length);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 扩充容量</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">resize</span><span class="params">(<span class="keyword">int</span> newCapacity)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//保存oldTable</span></span><br><span class="line">        Node[] oldTable = table;</span><br><span class="line">        <span class="comment">//保存旧容量</span></span><br><span class="line">        <span class="keyword">int</span> oldCapacity = oldTable.length;</span><br><span class="line">        <span class="comment">//如果旧的容量已经是系统默认最大容量了，那么将阈值设置成整形的最大值</span></span><br><span class="line">        <span class="keyword">if</span> (oldCapacity == MAXIMUM_CAPACITY) &#123;</span><br><span class="line">            threshold = Integer.MAX_VALUE;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//根据newCapacity创建一个table</span></span><br><span class="line">        Node[] newTable = <span class="keyword">new</span> Node[newCapacity];</span><br><span class="line">        <span class="comment">//将table转换为newTable</span></span><br><span class="line">        transfer(newTable);</span><br><span class="line">        table = newTable;</span><br><span class="line">        <span class="comment">//设置阈值</span></span><br><span class="line">        threshold = (<span class="keyword">int</span>) (newCapacity * loadFactor);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将所有格子里的桶都放到新的table中</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">transfer</span><span class="params">(Node[] newTable)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 得到旧的table</span></span><br><span class="line">        Node[] src = table;</span><br><span class="line">        <span class="comment">// 得到新的容量</span></span><br><span class="line">        <span class="keyword">int</span> newCapacity = newTable.length;</span><br><span class="line">        <span class="comment">// 遍历src里面的所有格子</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; src.length; j++) &#123;</span><br><span class="line">            <span class="comment">// 取到格子里的桶（也就是链表）</span></span><br><span class="line">            Node&lt;K, V&gt; e = src[j];</span><br><span class="line">            <span class="comment">// 如果e不为空</span></span><br><span class="line">            <span class="keyword">if</span> (e != <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="comment">// 将当前格子设成null</span></span><br><span class="line">                src[j] = <span class="keyword">null</span>;</span><br><span class="line">                <span class="comment">// 遍历格子的所有桶</span></span><br><span class="line">                <span class="keyword">do</span> &#123;</span><br><span class="line">                    <span class="comment">// 取出下个桶</span></span><br><span class="line">                    Node&lt;K, V&gt; next = e.next;</span><br><span class="line">                    <span class="comment">// 寻找新的索引</span></span><br><span class="line">                    <span class="keyword">int</span> i = indexFor(e.hash, newCapacity);</span><br><span class="line">                    <span class="comment">// 设置e.next为newTable[i]保存的桶（也就是链表连接上）</span></span><br><span class="line">                    e.next = newTable[i];</span><br><span class="line">                    <span class="comment">// 将e设成newTable[i]</span></span><br><span class="line">                    newTable[i] = e;</span><br><span class="line">                    <span class="comment">// 设置e为下一个桶</span></span><br><span class="line">                    e = next;</span><br><span class="line">                &#125; <span class="keyword">while</span> (e != <span class="keyword">null</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> V <span class="title">get</span><span class="params">(Object key)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//如果key为null,则调用getForNullkey()获得key为null的值</span></span><br><span class="line">        <span class="keyword">if</span> (key == <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">return</span> getForNullKey();</span><br><span class="line">        <span class="comment">//根据key的hashCode计算它的hash</span></span><br><span class="line">        <span class="keyword">int</span> hash = hash(key.hashCode());</span><br><span class="line">        <span class="comment">//取出table中指定索引处的值</span></span><br><span class="line">        <span class="keyword">for</span> (Node&lt;K, V&gt; e = table[indexFor(hash, table.length)]; e != <span class="keyword">null</span>; e = e.next) &#123;</span><br><span class="line">            Object k;</span><br><span class="line">            <span class="comment">//如果查找相同的key,返回其对应的value</span></span><br><span class="line">            <span class="keyword">if</span> (e.hash == hash &amp;&amp; ((k = e.key) == key || key.equals(k))) &#123;</span><br><span class="line">                <span class="keyword">return</span> e.value;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> V <span class="title">put</span><span class="params">(K key, V value)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//如果key为null,调用putForNullKey()向null key存入value</span></span><br><span class="line">        <span class="keyword">if</span> (key == <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">return</span> putForNullKey(value);</span><br><span class="line">        <span class="comment">//计算key的hash</span></span><br><span class="line">        <span class="keyword">int</span> hash = hash(key.hashCode());</span><br><span class="line">        <span class="comment">//计算key的hash在table数组中的索引</span></span><br><span class="line">        <span class="keyword">int</span> i = indexFor(hash, table.length);</span><br><span class="line">        <span class="comment">//遍历table</span></span><br><span class="line">        <span class="keyword">for</span> (Node&lt;K, V&gt; e = table[i]; e != <span class="keyword">null</span>; e = e.next) &#123;</span><br><span class="line">            Object k;</span><br><span class="line">            <span class="comment">//如果有相同的key,直接覆盖value,返回oldValue</span></span><br><span class="line">            <span class="keyword">if</span> (e.hash == hash &amp;&amp; ((k = e.key) == key || key.equals(k))) &#123;</span><br><span class="line">                V oldValue = e.value;</span><br><span class="line">                e.value = value;</span><br><span class="line">                <span class="keyword">return</span> oldValue;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        modCount++; <span class="comment">//修改次数++</span></span><br><span class="line">        <span class="comment">//将key,value添加至i处</span></span><br><span class="line">        addEntry(hash, key, value, i);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Set&lt;Entry&lt;K, V&gt;&gt; entrySet() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Map&lt;String, String&gt; map = <span class="keyword">new</span> SimpleHashMap&lt;&gt;();</span><br><span class="line">        map.put(<span class="string">&quot;hello&quot;</span>, <span class="string">&quot;world&quot;</span>);</span><br><span class="line">        System.out.println(map.get(<span class="string">&quot;hello&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2016/09/05/2016-09-5-HotSpotObject/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2016/09/05/2016-09-5-HotSpotObject/" class="post-title-link" itemprop="url">探秘HotSpot虚拟机中的对象</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2016-09-05 18:00:00" itemprop="dateCreated datePublished" datetime="2016-09-05T18:00:00+08:00">2016-09-05</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">后端</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/JVM/" itemprop="url" rel="index"><span itemprop="name">JVM</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="对象的创建"><a href="#对象的创建" class="headerlink" title="对象的创建"></a>对象的创建</h3><hr>
<p>&nbsp;&nbsp;在Java程序运行过程中无时无刻都有对象被创建出来.在语言层面上,创建对象通常仅仅是一个new关键字而已,而在虚拟机中,创建一个对象不像只需要new一下那么简单了.</p>
<p>&nbsp;&nbsp;以下为虚拟机中对象创建的过程(仅限于普通Java对象,不包括数组和Class对象等).</p>
<ol>
<li><p>虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过.如果没有,那必须先执行相应的类加载过程.</p>
</li>
<li><p>在类加载检查通过后,接下来虚拟机将为新生对象分配内存.对象所需内存的大小在类加载完成后便可完全确定,为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来.</p>
<ul>
<li><p>如果Java堆中内存是绝对规整的,所有用过的内存都放在一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离,这种分配方式称为”指针碰撞(Bump the Pointer)”.在使用Serial、ParNew等待Compact过程的收集器时,系统采用的分配算法为指针碰撞.</p>
</li>
<li><p>如果Java堆中内存并不是规整的,已使用的内存和空闲的内存相互交错,那就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为”空闲列表(Free List)”.在使用CMS这种基于Mark-Sweep算法的收集器,通常采用空闲列表.</p>
</li>
<li><p>由于创建对象是一件非常频繁的事情,所以除了划分可用空间之外,虚拟机还要考虑线程安全的问题,可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况.解决这个问题有两种方案,一种是对分配内存空间的动作进行同步处理(虚拟机采用CAS配上失败重试的方式保证更新操作的原子性);另一种是把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer TLAB).哪个线程要分配内存,就在哪个线程的TLAB上分配,只有TLAB用完并分配新的TLAB时,才需要同步锁定.虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设置.</p>
</li>
</ul>
</li>
<li><p>在内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),如果使用TLAB,这一工作也可以提前至TLAB分配时进行.这一步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值.</p>
</li>
<li><p>最后一步,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息.这些信息存放在对象的对象头(Object Header)之中.根据虚拟机当前的运作状态的不同,如是否启动偏向锁等,对象头会有不同的设置方式.</p>
</li>
<li><p>以上工作全部完成后,从虚拟机的角度来看,一个新的对象已经产生了,但从Java程序的视角来看,对象的创建才刚刚开始(init方法还没有执行,所有字段都还为零值).一般来说(由字节码中是否跟随invokespecial指令所决定),执行new指令之后会接着执行init()方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来.</p>
</li>
</ol>
<h3 id="对象的内存布局"><a href="#对象的内存布局" class="headerlink" title="对象的内存布局"></a>对象的内存布局</h3><hr>
<p>&nbsp;&nbsp;在HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Object Header)、实例数据(Instance Data)和对齐填充(Padding).</p>
<h4 id="对象头"><a href="#对象头" class="headerlink" title="对象头"></a>对象头</h4><p>&nbsp;&nbsp;对象头包括两部分信息,第一部分用于存储对象自身的运行时数据(例如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等),这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32bit和64bit,官方称其为”Mark Word”.这些运行时数据很多,其实已经超过了32位、64位Bitmap结构所能记录的限度.考虑到虚拟机的空间效率,Mark Word被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据对象的状态复用自己的存储空间.例如,在32位的HotSpot虚拟机中,如果对象处于未被锁定的状态下,那么Mark Word的32bit空间中的25bit用于存储对象哈希码,4bit用于存储对象分代年龄,2bit用于存储锁标志位,1bit固定为0,而在其他状态(轻量级锁定、重量级锁定、GC标记、可偏向)下对象的存储内容参见下表.</p>
<table>
<thead>
<tr>
<th>存储内容</th>
<th>标志位</th>
<th>状态</th>
</tr>
</thead>
<tbody><tr>
<td>对象哈希码、对象分代年龄</td>
<td>01</td>
<td>未锁定</td>
</tr>
<tr>
<td>指向锁记录的指针</td>
<td>00</td>
<td>轻量级锁定</td>
</tr>
<tr>
<td>指向重量级锁的指针</td>
<td>10</td>
<td>膨胀(重量级锁定)</td>
</tr>
<tr>
<td>空,不需要记录信息</td>
<td>11</td>
<td>GC标记</td>
</tr>
<tr>
<td>偏向线程ID、偏向时间戳、对象分代年龄</td>
<td>01</td>
<td>可偏向</td>
</tr>
</tbody></table>
<p>&nbsp;&nbsp;对象头的另一部分为类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例.并不是所有的虚拟机实现都必须在对象数据上保留类型指针,也可以认为,查找对象的元数据信息并不一定要经过对象本身.如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中无法确定数组的大小.</p>
<h4 id="实例数据"><a href="#实例数据" class="headerlink" title="实例数据"></a>实例数据</h4><p>&nbsp;&nbsp;实例数据是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容.无论是从父类继承下来的,还是子类中定义的,都需要记录起来.</p>
<p>&nbsp;&nbsp;实例数据的存储顺序会受到虚拟机分配策略参数(FieldsAllocationStyle)和字段在Java源码中定义顺序的影响.HotSpot虚拟机默认的分配策略为longs/doubles、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers),从以上分配策略中可以看出,相同宽度的字段总是被分配到一起.在满足这个前提条件的情况下,父类中定义的变量会出现在子类之前.如果CompactFields参数值为true(默认为true),那么子类中较窄的变量也可能会插入到父类变量的空隙之中.</p>
<h4 id="对齐填充"><a href="#对齐填充" class="headerlink" title="对齐填充"></a>对齐填充</h4><p>&nbsp;&nbsp;对齐填充并不是必然存在的,它也没有特别的含义,只是用于当作占位符而已.</p>
<p>&nbsp;&nbsp;因为HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数值,即是对象的大小必须是8字节的整数倍.而对象头部分正好是8字节的倍数(1倍或2倍),所以,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全.</p>
<h3 id="对象的访问定位"><a href="#对象的访问定位" class="headerlink" title="对象的访问定位"></a>对象的访问定位</h3><hr>
<p>&nbsp;&nbsp;Java程序需要通过栈上的reference数据来操作堆上的具体对象.由于reference类型在Java虚拟机规范中只规定了一个指向对象的引用,所以对象访问方式也是取决于虚拟机实现而定的.</p>
<p>&nbsp;&nbsp;目前主流的访问方式为句柄和直接指针两种.</p>
<h4 id="句柄"><a href="#句柄" class="headerlink" title="句柄"></a>句柄</h4><p><img src="http://ww1.sinaimg.cn/mw690/63503acbjw1f7l9o8a5qpj20tw0fhmyc.jpg" alt="通过句柄访问对象"></p>
<p>&nbsp;&nbsp;使用句柄访问对象,Java堆中将会划分出一块内存作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息.</p>
<p>&nbsp;&nbsp;使用句柄来访问的最大好处就是reference中存储的是稳定的句柄地址,在对象被移动时(垃圾收集时移动对象是非常普遍的行为)只会改变句柄中的实例数据指针,而reference本身不需要修改.</p>
<h4 id="直接指针"><a href="#直接指针" class="headerlink" title="直接指针"></a>直接指针</h4><p><img src="http://ww2.sinaimg.cn/mw690/63503acbjw1f7l9o8m90vj20tw0fhq3w.jpg" alt="通过直接指针访问对象"></p>
<p>&nbsp;&nbsp;使用直接指针访问对象,Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而reference中存储的直接就是对象地址.</p>
<p>&nbsp;&nbsp;使用直接指针访问方式的最大好处就是速度更快,它节省了一次指针定位的时间开销,由于对象的访问在Java中非常频繁,积少成多后也是一项非常可观的执行成本.HotSpot虚拟机就是使用直接指针方式进行对象访问的.</p>
<h3 id="End"><a href="#End" class="headerlink" title="End"></a>End</h3><hr>
<blockquote>
<p>资料参考于 &lt;&lt;深入理解 Java虚拟机 第二版&gt;&gt;.</p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2016/09/04/2016-09-4-JavaMemoryRegion/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2016/09/04/2016-09-4-JavaMemoryRegion/" class="post-title-link" itemprop="url">Java虚拟机中的内存区域</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2016-09-04 18:00:00" itemprop="dateCreated datePublished" datetime="2016-09-04T18:00:00+08:00">2016-09-04</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">后端</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/JVM/" itemprop="url" rel="index"><span itemprop="name">JVM</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><hr>
<p>&nbsp;&nbsp;对于C/C++程序员来说需要自己负责每一个对象生命开始到终结的维护.而对于Java程序员来说,则可以在Java虚拟机自动内存管理机制的帮助下,不需要为每一个new的对象去写delete/free代码,由虚拟机管理内存可以让我们把注意力放在实现业务逻辑上.</p>
<p>&nbsp;&nbsp;但也正是因为Java虚拟机接管了内存控制的权力,所以一旦出现内存泄漏或溢出方面的异常问题,就需要了解虚拟机如何使用与维护内存,这样才能更好的排查错误解决异常.</p>
<h3 id="运行时数据区域"><a href="#运行时数据区域" class="headerlink" title="运行时数据区域"></a>运行时数据区域</h3><hr>
<p>&nbsp;&nbsp;Java虚拟机在执行Java程序时会把它所管理的内存划分为若干个不同的数据区域.这些区域都有着各自的用途,以及创建和销毁的时间,有的区域则会随着虚拟机进程的启动而存在,有些区域则会依赖于用户线程的启动和结束而创建和销毁.</p>
<p>&nbsp;&nbsp;根据&lt;&lt;Java 虚拟机规范&gt;&gt;的规定,Java虚拟机管理的内存将会包括以下图所示的几个运行时数据区域.</p>
<p><img src="http://ww2.sinaimg.cn/mw690/63503acbjw1f7j0psfbuaj20l90degne.jpg" alt="运行时数据区域"></p>
<h4 id="程序计数器"><a href="#程序计数器" class="headerlink" title="程序计数器"></a>程序计数器</h4><p>&nbsp;&nbsp;程序计数器(Program Counter Register)可以看作为当前线程所执行的字节码的行号指示器,它占用的内存很小.</p>
<p>&nbsp;&nbsp;字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖程序计数器来完成.</p>
<p>&nbsp;&nbsp;在Java虚拟机中,多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的.在任何一个确定的时刻,一个处理器(或多核CPU中的一个内核)都只会执行一条线程中的指令.为了线程切换后能够恢复到正确的执行位置,每一条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,这种类型的内存区域为”线程私有”的内存.</p>
<p>&nbsp;&nbsp;如果线程正在执行的是一个Java方法,程序计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是一个Native方法,程序计数器值则为空(Undefined).</p>
<p>&nbsp;&nbsp;程序计数器内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError异常情况的区域.</p>
<h4 id="虚拟机栈"><a href="#虚拟机栈" class="headerlink" title="虚拟机栈"></a>虚拟机栈</h4><p>&nbsp;&nbsp;Java虚拟机栈(Java Virtual Machine Stacks)与程序计数器一样,是线程私有的内存,它的生命周期与线程相同.</p>
<p>&nbsp;&nbsp;虚拟机栈描述的是Java方法执行的内存模型:即每个Java方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息.栈帧是方法运行时的基础数据结构.</p>
<p>&nbsp;&nbsp;每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程.</p>
<p>&nbsp;&nbsp;栈帧中的局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用类型(reference类型,它不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址).</p>
<p>&nbsp;&nbsp;64位长度的long和double类型的数据会占用2个局部变量空间(Slot),其他的数据类型只占用1个.局部变量表所需的内存空间是在编译期间完成分配的.当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小.</p>
<p>&nbsp;&nbsp;Java虚拟机规范对虚拟机栈区域规定了两种异常情况:</p>
<ol>
<li><p>如果线程请求的栈深度大于虚拟机所允许的深度,将会抛出StackOverflowError异常.</p>
</li>
<li><p>如果虚拟机可以动态扩展,并在扩展时无法申请到足够的内存时,将会抛出OutOfMemoryError异常.</p>
</li>
</ol>
<h4 id="本地方法栈"><a href="#本地方法栈" class="headerlink" title="本地方法栈"></a>本地方法栈</h4><p>&nbsp;&nbsp;本地方法栈(Native Method Stack)与虚拟机栈基本相似.它们之间的区别只不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务.</p>
<p>&nbsp;&nbsp;Java虚拟机规范对本地方法栈没有强制的规定,具体的虚拟机可以自由实现它.例如Sun HotSpot虚拟机甚至将本地方法栈与虚拟机栈合二为一.</p>
<p>&nbsp;&nbsp;与虚拟机栈相同,本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常.</p>
<h4 id="Java堆"><a href="#Java堆" class="headerlink" title="Java堆"></a>Java堆</h4><p>&nbsp;&nbsp;在大多数情况下,Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块.它是被所有线程共享的一块内存区域,在虚拟机启动时创建.Java堆的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存.</p>
<p>&nbsp;&nbsp;但随着JIT编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化发生,所有对象都分配在堆上也渐渐变得不是那么”绝对”了.</p>
<p>&nbsp;&nbsp;Java堆也是垃圾收集器管理的主要区域,也可以称为GC堆(Garbage Collected Heap).由于现在垃圾收集器基本都采用分代收集算法,所以Java堆中可以细分为新生代和老年代(再细化的有Eden空间、Form Survivor空间、To Survivor空间等).</p>
<p>&nbsp;&nbsp;从内存分配的角度来看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer TLAB).</p>
<p>&nbsp;&nbsp;Java虚拟机规范规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可.在实现时,既可以实现是固定大小的,也可以是可扩展的,如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常.</p>
<h4 id="方法区"><a href="#方法区" class="headerlink" title="方法区"></a>方法区</h4><p>&nbsp;&nbsp;方法区(Method Area)与Java堆一样是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据.Java虚拟机规范中把方法区描述为堆的一个逻辑部分,但它有一个别名叫Non-Heap(非堆),用于与Java堆区分开来.</p>
<p>&nbsp;&nbsp;在HotSpot虚拟机中,方法区被称作为”永久代(Permanent Generation)”.因为HotSpot将GC分代收集扩展至了方法区,或者说是使用永久代来实现方法区.这样HotSpot的垃圾收集器可以像管理Java堆一样管理这部分内存,能够省去编写方法区的内存管理代码工作.但这样做法会更容易遇到内存溢出问题.所以HotSpot官方决定放弃永久代并逐步改为采用Native Memory来实现方法区的规划.在JDK1.7的HotSpot中,已经把原本放在永久代的字符串常量池移出.</p>
<p>&nbsp;&nbsp;Java虚拟机规范规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常.</p>
<h4 id="运行时常量池"><a href="#运行时常量池" class="headerlink" title="运行时常量池"></a>运行时常量池</h4><p>&nbsp;&nbsp;运行时常量池(Runtime Constant Pool)是方法区的一部分.Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),它用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放.一般来说,除了保存Class文件中描述的符号引用外,还会把翻译出来的直接引用也存储在运行时常量池中.</p>
<p>&nbsp;&nbsp;运行时常量池相对于Class文件中常量池的另外一个重要特征是具备动态性.Java语言并不要求常量一定只有编译期才能产生,运行期间也可能将新的常量放入池中,这种特性利用得比较多的便是String类的intern()方法.</p>
<p>&nbsp;&nbsp;当运行时常量池无法申请到内存时会抛出OutOfMemoryError异常.</p>
<h4 id="直接内存"><a href="#直接内存" class="headerlink" title="直接内存"></a>直接内存</h4><p>&nbsp;&nbsp;直接内存(Direct Memory)并不是虚拟机运行时数据区域的一部分,也不是Java虚拟机规范中定义的内存区域.但是部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常.</p>
<p>&nbsp;&nbsp;在JDK1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作.这样避免了在Java堆中和Native堆中来回复制数据,提高了性能.</p>
<p>&nbsp;&nbsp;由于本机直接内存不会受到Java堆大小的限制,但它还是会受到本机总内存大小以及处理器寻址空间的限制.当我们配置虚拟机参数时,会根据实际内存设置-Xmx等参数信息,但经常会忽略直接内存,导致各个内存区域总和大于物理内存限制,从而导致动态扩展时出现OutOfMemoryError异常.</p>
<h3 id="OutOfMemoryError异常案例"><a href="#OutOfMemoryError异常案例" class="headerlink" title="OutOfMemoryError异常案例"></a>OutOfMemoryError异常案例</h3><hr>
<h4 id="Java堆溢出"><a href="#Java堆溢出" class="headerlink" title="Java堆溢出"></a>Java堆溢出</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Java堆内存溢出,Java堆内存的OOM异常是实际应用中常见的内存溢出异常情况.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 堆内存用于存储对象实例,只要不断地创建对象,并且保证GC Roots到对象之间有可达路径来避免</span></span><br><span class="line"><span class="comment"> * 垃圾回收机制清除这些对象,这样就可以在对象数量到达最大堆的容量限制后产生内存溢出异常.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 以下启动参数中,-Xms20 -Xmx20m 将堆的最小值与最大值设置为一样的,既可避免自动扩展.</span></span><br><span class="line"><span class="comment"> * -XX:+HeapDumpOnOutOfMemoryError可以让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照以便分析.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * Created by sylvanasp on 2016/9/4.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HeapOOM</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">OOMObject</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        List&lt;OOMObject&gt; list = <span class="keyword">new</span> ArrayList&lt;OOMObject&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">            list.add(<span class="keyword">new</span> OOMObject());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h4 id="虚拟机栈溢出"><a href="#虚拟机栈溢出" class="headerlink" title="虚拟机栈溢出"></a>虚拟机栈溢出</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 在单线程中,以下两种方法均无法让虚拟机产生OutOfMemoryError异常,尝试的结果均为StackOverflowError异常.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 1.使用-Xss参数减少栈内存容量.结果为StackOverflowError异常,出现时输出的堆栈深度相应缩小.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * VM Args: -Xss128k</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Created by sylvanasp on 2016/9/4.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JavaVMStackSOF</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> stackLength = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 定义大量的本地变量,增大此方法帧中本地变量表的长度.</span></span><br><span class="line"><span class="comment">     * 结果:抛出StackOverflowError异常时输出的堆栈深度相应缩小.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">stackLeak</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        stackLength++;</span><br><span class="line">        stackLeak();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Throwable </span>&#123;</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 在单线程下,无论是栈帧太大还是虚拟机栈容量太小,当内存无法分配时,</span></span><br><span class="line"><span class="comment">         * 虚拟机抛出的都是StackOverflowError异常.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        JavaVMStackSOF javaVMStackSOF = <span class="keyword">new</span> JavaVMStackSOF();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            javaVMStackSOF.stackLeak();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable e) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;stack length:&quot;</span> + javaVMStackSOF.stackLength);</span><br><span class="line">            <span class="keyword">throw</span> e;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 通过不断建立线程的方式产生内存溢出异常.</span></span><br><span class="line"><span class="comment"> * 但是这样产生的内存溢出异常与栈空间是否足够大并不存在任何联系.</span></span><br><span class="line"><span class="comment"> * 在这种情况下,为每个线程的栈分配的内存越大,反而越容易产生内存溢出异常.</span></span><br><span class="line"><span class="comment"> * 因为内存最后才由虚拟机栈和本地方法栈&quot;瓜分&quot;.</span></span><br><span class="line"><span class="comment"> * 所以每个线程分配到的栈容量越大,可以建立的线程数量自然就越少,建立线程时就越容易把剩下的内存耗尽。</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 如果是建立过多线程导致的内存溢出,在不能减少线程数或者更换64位虚拟机的情况下.</span></span><br><span class="line"><span class="comment"> * 就只能通过减少最大堆和减少栈容量来换取更多的线程.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * VM Args: -Xss2M</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * Created by sylvanasp on 2016/9/4.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JavaVMStackOOM</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">dontStop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">stackLeakByThread</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">            Thread thread = <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                    dontStop();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;);</span><br><span class="line">            thread.start();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        JavaVMStackOOM javaVMStackOOM = <span class="keyword">new</span> JavaVMStackOOM();</span><br><span class="line">        javaVMStackOOM.stackLeakByThread();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h4 id="方法区溢出"><a href="#方法区溢出" class="headerlink" title="方法区溢出"></a>方法区溢出</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 方法区内存溢出</span></span><br><span class="line"><span class="comment"> * 方法区是用于存放Class的相关信息的,所以要让方法区产生内存溢出的基本思路为:</span></span><br><span class="line"><span class="comment"> * 在运行时产生大量的类去填满方法区,直到溢出.</span></span><br><span class="line"><span class="comment"> * 所以可以使用CGLib直接操作字节码运行时生成大量的动态类.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * VM Args : -XX:PermSize=10M -XX:MaxPermSize=10M</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Created by sylvanasp on 2016/9/4.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JavaMethodAreaOOM</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">OOMObject</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">final</span> String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">            Enhancer enhancer = <span class="keyword">new</span> Enhancer();</span><br><span class="line">            enhancer.setSuperclass(OOMObject.class);</span><br><span class="line">            enhancer.setUseCache(<span class="keyword">false</span>);</span><br><span class="line">            enhancer.setCallback(<span class="keyword">new</span> MethodInterceptor() &#123;</span><br><span class="line">                <span class="function"><span class="keyword">public</span> Object <span class="title">intercept</span><span class="params">(Object o, Method method, Object[] objects,</span></span></span><br><span class="line"><span class="function"><span class="params">                                        MethodProxy methodProxy)</span> <span class="keyword">throws</span> Throwable </span>&#123;</span><br><span class="line">                    <span class="keyword">return</span> methodProxy.invokeSuper(o,objects);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;);</span><br><span class="line">            enhancer.create();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h4 id="运行时常量池溢出"><a href="#运行时常量池溢出" class="headerlink" title="运行时常量池溢出"></a>运行时常量池溢出</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 方法区和运行时常量池溢出</span></span><br><span class="line"><span class="comment"> * 在JDK1.6及之前的版本中,由于常量池分配在永久代内.</span></span><br><span class="line"><span class="comment"> * 所以可以通过 -XX:PermSize和-XX:MaxPermSize限制方法区大小.</span></span><br><span class="line"><span class="comment"> * 从而间接限制其中常量池的容量.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 而使用JDK1.7运行这段程序则不会得到相同的结果,while循环将一直进行下去.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * VM Args : -XX:PermSize=10M -XX:MaxPermSize=10M</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Created by sylvanasp on 2016/9/4.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RuntimeConstantPoolOOM</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 使用List保持常量池的引用,避免Full GC回收常量池行为.</span></span><br><span class="line">        List&lt;String&gt; list = <span class="keyword">new</span> ArrayList&lt;String&gt;();</span><br><span class="line">        <span class="keyword">int</span> i = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">            <span class="comment">/**</span></span><br><span class="line"><span class="comment">             * String.intern()是一个Native方法.</span></span><br><span class="line"><span class="comment">             * 它的作用是:</span></span><br><span class="line"><span class="comment">             * 如果字符串常量池中已经包含了一个等于此String对象的字符串,则返回代表池中这个字符串的String对象.</span></span><br><span class="line"><span class="comment">             * 否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用.</span></span><br><span class="line"><span class="comment">             */</span></span><br><span class="line">            list.add(String.valueOf(i++).intern());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 这段代码在JDK1.6中运行,会得到两个false,而在JDK1.7中运行,会得到一个true和一个false.</span></span><br><span class="line"><span class="comment">     * 产生差异的原因为:</span></span><br><span class="line"><span class="comment">     * 在JDK1.6中,intern()方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中这个字符串实例的引用.</span></span><br><span class="line"><span class="comment">     * 而由StringBuilder创建的字符串实例在Java堆上,所以必然不是同一个引用.所以返回false.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * 在JDK1.7中(或部分其他虚拟机,如JRockit),intern()实现不会再复制实例.</span></span><br><span class="line"><span class="comment">     * 只是在常量池中记录首次出现的实例引用,因此intern()返回的引用和由StringBuilder创建的字符串实例为同一个.</span></span><br><span class="line"><span class="comment">     * 对str2比较返回false是因为&quot;java&quot;这个字符串在执行StringBuilder.toString()之前已经出现过,</span></span><br><span class="line"><span class="comment">     * 字符串常量池中已经有它的引用了,不符合&quot;首次出现&quot;的原则.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">stringPool</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        String str1 = <span class="keyword">new</span> StringBuilder(<span class="string">&quot;Hello&quot;</span>).append(<span class="string">&quot;World哈哈&quot;</span>).toString();</span><br><span class="line">        System.out.println(str1.intern() == str1);</span><br><span class="line"></span><br><span class="line">        String str2 = <span class="keyword">new</span> StringBuilder(<span class="string">&quot;ja&quot;</span>).append(<span class="string">&quot;va&quot;</span>).toString();</span><br><span class="line">        System.out.println(str2.intern() == str2);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h4 id="直接内存溢出"><a href="#直接内存溢出" class="headerlink" title="直接内存溢出"></a>直接内存溢出</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 直接内存溢出</span></span><br><span class="line"><span class="comment"> * DirectMemory容量可以通过 -XX:MaxDirectMemorySize指定.</span></span><br><span class="line"><span class="comment"> * 如果不指定,则默认与Java堆最大值(-Xmx)一致.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * VM Args : -Xmx20M -XX:MaxDirectMemorySize=10M</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Created by sylvanasp on 2016/9/4.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DirectMemoryOOM</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> _1MB = <span class="number">1024</span> * <span class="number">1024</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 越过DirectByteBuffer类,直接通过反射获取Unsafe实例进行内存分配.</span></span><br><span class="line"><span class="comment">     * Unsafe.getUnsafe()方法限制了只有引导类加载器才会返回实例,</span></span><br><span class="line"><span class="comment">     * 即只有rt.jar中的类才能使用Unsafe的功能.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * 使用DirectByteBuffer类分配内存虽然也会抛出内存溢出的异常,</span></span><br><span class="line"><span class="comment">     * 但它抛出异常时并没有真正的向操作系统申请分配内存,而是通过计算得知内存无法分配,于是手动抛出异常.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * 而unsafe.allocateMemory()则可以真正申请分配内存.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IllegalAccessException </span>&#123;</span><br><span class="line"></span><br><span class="line">        Field unsafeField = Unsafe.class.getDeclaredFields()[<span class="number">0</span>];</span><br><span class="line">        unsafeField.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">        Unsafe unsafe = (Unsafe) unsafeField.get(<span class="keyword">null</span>);</span><br><span class="line">        <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">            unsafe.allocateMemory(_1MB);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3 id="End"><a href="#End" class="headerlink" title="End"></a>End</h3><hr>
<blockquote>
<p>资料参考于 &lt;&lt;深入理解 Java虚拟机 第二版&gt;&gt;.</p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2016/09/03/2016-09-3-CompilerOpenJDK/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2016/09/03/2016-09-3-CompilerOpenJDK/" class="post-title-link" itemprop="url">在CentOS7下手动编译JDK</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2016-09-03 18:00:00" itemprop="dateCreated datePublished" datetime="2016-09-03T18:00:00+08:00">2016-09-03</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">后端</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/JVM/" itemprop="url" rel="index"><span itemprop="name">JVM</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="下载OpenJDK源码"><a href="#下载OpenJDK源码" class="headerlink" title="下载OpenJDK源码"></a>下载OpenJDK源码</h3><p>&nbsp;&nbsp;oepnjdk下载地址为 <a target="_blank" rel="noopener" href="https://jdk7.java.net/source.html">https://jdk7.java.net/source.html</a></p>
<h3 id="安装编译依赖环境"><a href="#安装编译依赖环境" class="headerlink" title="安装编译依赖环境"></a>安装编译依赖环境</h3><ol>
<li><p>安装alsa包.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum install alsa-lib-devel</span><br></pre></td></tr></table></figure>
</li>
<li><p>安装cups-devel </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum install cups-devel</span><br></pre></td></tr></table></figure>
</li>
<li><p>安装X相关库</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum install libX*</span><br></pre></td></tr></table></figure>
</li>
<li><p>安装gcc-c++    </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum install gcc gcc-c++</span><br></pre></td></tr></table></figure>
</li>
<li><p>安装freetype </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">rpm -ivh freetype-2.4.11-9.el7.x86_64.rpm</span><br><span class="line">下载地址:http:&#x2F;&#x2F;rpm.pbone.net&#x2F;index.php3&#x2F;stat&#x2F;4&#x2F;idpl&#x2F;26641422&#x2F;dir&#x2F;centos_7&#x2F;com&#x2F;freetype-2.4.11-9.el7.x86_64.rpm.html</span><br></pre></td></tr></table></figure>
</li>
<li><p>安装Ant</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">tar -zvxf apache-ant-1.9.6-bin.tar.gz</span><br><span class="line">下载地址:http:&#x2F;&#x2F;ant.apache.org&#x2F;bindownload.cgi</span><br></pre></td></tr></table></figure>

</li>
</ol>
<p>&nbsp;&nbsp;也可以使用以下命令一次性完成依赖安装:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">yum -y install build-essential gawk m4</span><br><span class="line">openjkd-6-jkd libasound2-dev libcups2-dev </span><br><span class="line">libxrender-dev xorg-dev xutils-dev</span><br><span class="line">xllproto-print-dev binutils libmotif3</span><br><span class="line">libmotif-dev ant</span><br></pre></td></tr></table></figure>
<h3 id="配置环境变量"><a href="#配置环境变量" class="headerlink" title="配置环境变量"></a>配置环境变量</h3><p>&nbsp;&nbsp;OpenJDK在编译时需要读取很多环境变量,但大部分都是有默认值的,必须设置的只有两个环境变量: LANG和ALT_BOOTDIR.</p>
<p>&nbsp;&nbsp;<strong>LANG</strong>是用来设定语言选项的,必须设置为: <strong>export LANG=C</strong>.</p>
<p>&nbsp;&nbsp;<strong>ALT_BOOTDIR</strong>则是用来设置Bootstrap JDK的位置,Bootstrap JDK就是一个可用的6u14以上版本的JDK,它是用来编译OpenJDK中部分Java实现的代码的.</p>
<p>&nbsp;&nbsp;另外,如果之前设置了JAVA_HOME和CLASSPATH两个环境变量,在编译之前必须取消,否则在Makefile脚本中检查到有这两个变量存在,会有警告提示.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">unset JAVA_HOME</span><br><span class="line">unset CLASSPATH</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;其他环境变量如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">#语言选项，这个必须设置，否则编译好后会出现一个HashTable的NPE错</span><br><span class="line">export LANG&#x3D;C</span><br><span class="line"></span><br><span class="line">#Bootstrap JDK的安装路径。必须设置。 </span><br><span class="line">export ALT_BOOTDIR&#x3D;&#x2F;Library&#x2F;Java&#x2F;JavaVirtualMachines&#x2F;jdk1.7.0_04.jdk&#x2F;Contents&#x2F;Home</span><br><span class="line"></span><br><span class="line">#允许自动下载依赖</span><br><span class="line">export ALLOW_DOWNLOADS&#x3D;true</span><br><span class="line"></span><br><span class="line">#并行编译的线程数，设置为和CPU内核数量一致即可</span><br><span class="line">export HOTSPOT_BUILD_JOBS&#x3D;6</span><br><span class="line">export ALT_PARALLEL_COMPILE_JOBS&#x3D;6</span><br><span class="line"></span><br><span class="line">#比较本次build出来的映像与先前版本的差异。这个对我们来说没有意义，必须设置为false，否则sanity检查会报缺少先前版本JDK的映像。如果有设置dev或者DEV_ONLY&#x3D;true的话这个不显式设置也行。 </span><br><span class="line">export SKIP_COMPARE_IMAGES&#x3D;true</span><br><span class="line"></span><br><span class="line">#使用预编译头文件，不加这个编译会更慢一些</span><br><span class="line">export USE_PRECOMPILED_HEADER&#x3D;true</span><br><span class="line"></span><br><span class="line">#要编译的内容</span><br><span class="line">export BUILD_LANGTOOLS&#x3D;true </span><br><span class="line">#export BUILD_JAXP&#x3D;false</span><br><span class="line">#export BUILD_JAXWS&#x3D;false </span><br><span class="line">#export BUILD_CORBA&#x3D;false</span><br><span class="line">export BUILD_HOTSPOT&#x3D;true </span><br><span class="line">export BUILD_JDK&#x3D;true</span><br><span class="line"></span><br><span class="line">#要编译的版本</span><br><span class="line">#export SKIP_DEBUG_BUILD&#x3D;false</span><br><span class="line">#export SKIP_FASTDEBUG_BUILD&#x3D;true</span><br><span class="line">#export DEBUG_NAME&#x3D;debug</span><br><span class="line"></span><br><span class="line">#把它设置为false可以避开javaws和浏览器Java插件之类的部分的build。 </span><br><span class="line">BUILD_DEPLOY&#x3D;false</span><br><span class="line"></span><br><span class="line">#把它设置为false就不会build出安装包。因为安装包里有些奇怪的依赖，但即便不build出它也已经能得到完整的JDK映像，所以还是别build它好了。</span><br><span class="line">BUILD_INSTALL&#x3D;false</span><br><span class="line"></span><br><span class="line">#编译结果所存放的路径</span><br><span class="line">export ALT_OUTPUTDIR&#x3D;&#x2F;Users&#x2F;IcyFenix&#x2F;Develop&#x2F;JVM&#x2F;jdkBuild&#x2F;openjdk_7u4&#x2F;build</span><br></pre></td></tr></table></figure>
<h3 id="编译JDK"><a href="#编译JDK" class="headerlink" title="编译JDK"></a>编译JDK</h3><ul>
<li><p>&nbsp;&nbsp;全部设置好之后,可用在OpenJDK目录下使用 make sanity来检查设置是否正确,如果正确则会会输出：Sanity check passed.</p>
</li>
<li><p>&nbsp;&nbsp;之后,输入make命令开始编译,(make不添加参数默认为编译make all).</p>
</li>
<li><p>&nbsp;&nbsp;编译完成后,可以将目录复制到JAVA_HOME中,作为一个完整的JDK使用,编译出来的虚拟机,在-version命令中带有用户的机器名.</p>
</li>
<li><p>&nbsp;&nbsp;如果只想单独编译HotSpot的话，那么使用hotspot/make目录下的MakeFile进行替换即可，其他参数与前面一致，这时候虚拟机的输出结果存放在build/hotspot/outputdir/linux_amd64_compiler2 中，里面对应了不同的优化级别的目录.</p>
</li>
<li><p>&nbsp;&nbsp;在不同机器上,最后一个目录名称会有所差别,bad表示Mac OS系统(内核为FreeBSD),amd64表示是64位JDK(32位为x86),compiler2表示是Server VM(Client VM表示是compiler1).</p>
</li>
<li><p>&nbsp;&nbsp;在运行虚拟机前，还要手工编辑目录下的env.sh文件，这个文件由编译脚本自动产生，用于设置虚拟机的环境变量，里面已经发布了”JAVA_HOME，CLASSPATH，HOTSPOT_BUILD_USER” 3个环境变量，还需要增加一个“LD_LIBRARY_CLASSPATH”,内容如下:</p>
<p>-</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">LD_LIBRARY_PATH&#x3D;.:$&#123;JAVA_HOME&#125;&#x2F;jre&#x2F;lib&#x2F;amd64&#x2F;native_threads:$&#123;JAVA_HOME&#125;&#x2F;jre&#x2F;lib&#x2F;amd64:</span><br><span class="line">export LD_LIBRARY_PATH</span><br></pre></td></tr></table></figure></li>
<li><p>&nbsp;&nbsp;然后执行以下命令启动虚拟机(这时的启动机器名为gamma).</p>
<p>-</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">. .&#x2F;env.sh</span><br><span class="line">.&#x2F;gamma -version   #有可能是test_gamma,这是自带的一段八皇后代码 </span><br></pre></td></tr></table></figure>
<h3 id="Exception"><a href="#Exception" class="headerlink" title="Exception"></a>Exception</h3></li>
</ul>
<ol>
<li><p>报错:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Error: time is more than 10 years from present: 1104530400000 when building java&#x2F;openjdk* lists.freebsd.org</span><br></pre></td></tr></table></figure>

<p>通过修改CurrencyData.properties文件, 把10年之前的时间修改为10年之内即可<br>Index: /usr/openjdk/jdk/src/share/classes/java/util/CurrencyData.properties把2006改掉就可以重新编译了.</p>
</li>
</ol>
<h3 id="End"><a href="#End" class="headerlink" title="End"></a>End</h3><blockquote>
<p>资料参考于 &lt;&lt;深入理解 Java虚拟机 第二版&gt;&gt;.</p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2016/08/05/2016-08-5-Spring-monitor/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2016/08/05/2016-08-5-Spring-monitor/" class="post-title-link" itemprop="url">Spring Boot 部署测试与应用监控</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2016-08-05 18:00:00" itemprop="dateCreated datePublished" datetime="2016-08-05T18:00:00+08:00">2016-08-05</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">后端</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/Spring-Boot/" itemprop="url" rel="index"><span itemprop="name">Spring Boot</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="Spring-Boot部署"><a href="#Spring-Boot部署" class="headerlink" title="Spring Boot部署"></a>Spring Boot部署</h3><hr>
<h4 id="Jar包形式"><a href="#Jar包形式" class="headerlink" title="Jar包形式"></a>Jar包形式</h4><hr>
<p>&nbsp;&nbsp;如果在创建Spring Boot项目的时候选择的是jar包形式,则只需要使用maven将项目打成jar包即可.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mvn pakage</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;Spring Boot项目在打成jar后可以直接运行.</p>
<h4 id="War包形式"><a href="#War包形式" class="headerlink" title="War包形式"></a>War包形式</h4><hr>
<p>&nbsp;&nbsp;如果在创建Spring Boot项目的时候选择的是war包形式,则打包的方式与jar一样.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mvn pakage</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;之后将生成的war文件放在任意Servlet容器上运行即可.</p>
<p>&nbsp;&nbsp;如果在创建Spring Boot项目的时候选择的是jar,在部署时又想以war包形式部署,则需要以下配置.</p>
<ol>
<li><p>修改pom.xml文件的打包方式</p>
<p><img src="http://ww4.sinaimg.cn/mw690/63503acbjw1f6nkz6y4ntj20bm038mxv.jpg"></p>
</li>
<li><p>添加以下依赖覆盖默认内嵌的Tomcat.</p>
<p><img src="http://ww3.sinaimg.cn/mw690/63503acbjw1f6nkz77e95j20fy03w758.jpg"></p>
</li>
<li><p>创建ServletInitializer.</p>
<p><img src="http://ww4.sinaimg.cn/mw690/63503acbjw1f6nkz7gwh7j20o104z75l.jpg"></p>
</li>
</ol>
<h4 id="注册为Linux服务"><a href="#注册为Linux服务" class="headerlink" title="注册为Linux服务"></a>注册为Linux服务</h4><hr>
<p>&nbsp;&nbsp;将软件注册为服务,可以通过命令开启、关闭、开机自启动等功能.</p>
<p>&nbsp;&nbsp;如想注册为Linux服务,则先需要修改spring-boot-maven-plugin的配置:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">   &lt;build&gt;</span><br><span class="line">	&lt;plugins&gt;</span><br><span class="line">		&lt;plugin&gt;</span><br><span class="line">			&lt;groupId&gt;org.springframework.boot&lt;&#x2F;groupId&gt;</span><br><span class="line">			&lt;artifactId&gt;spring-boot-maven-plugin&lt;&#x2F;artifactId&gt;</span><br><span class="line">			&lt;configuration&gt;</span><br><span class="line">				&lt;executable&gt;true&lt;&#x2F;executable&gt;</span><br><span class="line">			&lt;&#x2F;configuration&gt;</span><br><span class="line">		&lt;&#x2F;plugin&gt;</span><br><span class="line">	&lt;&#x2F;plugins&gt;</span><br><span class="line">&lt;&#x2F;build&gt;</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;主流的Linux大都使用init.d或systemd来注册服务.</p>
<ul>
<li><p>init.d方式</p>
<ol>
<li><p>sudo ln -s /var/apps/spring-boot-demo.jar /etc/init.d/sdemo,其中sdemo就是服务名.</p>
</li>
<li><p>常用操作命令</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">service sdemo start 启动服务.</span><br><span class="line">service sdemo stop 停止服务.</span><br><span class="line">service sdemo status 服务状态.</span><br><span class="line">chkconfig sdemo on 开机启动.</span><br></pre></td></tr></table></figure></li>
<li><p>项目日志存放于/var/log/sdemo.log中.</p>
</li>
</ol>
</li>
<li><p>systemd方式</p>
<ol>
<li><p>在/etc/systemd/system/目录下新建文件sdemo.service.并写入以下内容:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">[Unit]</span><br><span class="line">Description&#x3D;spring-boot-demo</span><br><span class="line">After&#x3D;syslog.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">ExecStart&#x3D; &#x2F;usr&#x2F;bin&#x2F;java -jar &#x2F;var&#x2F;apps&#x2F;spring-boot-demo.jar</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy&#x3D;multi-user.target</span><br><span class="line"></span><br><span class="line">#其中Description和ExecStart是可更改的.ExecStart是指定java运行需要运行的jar包.</span><br></pre></td></tr></table></figure>
</li>
<li><p>常用操作命令</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">systemctl start sdemo 启动服务.</span><br><span class="line">systemctl stop sdemo 停止服务.</span><br><span class="line">systemctl status sdemo 服务状态.</span><br><span class="line">systemctl enable sdemo 开机启动.</span><br><span class="line">journalctl -u sdemo 查看项目日志.</span><br></pre></td></tr></table></figure>

</li>
</ol>
</li>
</ul>
<h3 id="热部署"><a href="#热部署" class="headerlink" title="热部署"></a>热部署</h3><hr>
<p>&nbsp;&nbsp;热部署即就是在应用正在运行的时候升级软件，却不需要重新启动应用.</p>
<p>&nbsp;&nbsp;在Spring Boot项目中添加<strong>spring-boot-devtools</strong>依赖即可实现页面和类的热部署.</p>
<h4 id="模板引擎热部署"><a href="#模板引擎热部署" class="headerlink" title="模板引擎热部署"></a>模板引擎热部署</h4><hr>
<p>&nbsp;&nbsp;Spring Boot默认模板引擎开启缓存,如果我们修改了页面后再刷新页面将得不到修改后的内容,我们可以在application.properties中关闭模板引擎的缓存.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">#Thymeleaf</span><br><span class="line">spring.thymeleaf.cache&#x3D;false</span><br><span class="line">#FreeMarker</span><br><span class="line">spring.freemarker.cache&#x3D;false</span><br><span class="line">#Groovy</span><br><span class="line">spring.groovy.template.cache&#x3D;false</span><br><span class="line">#Velocity</span><br><span class="line">spring.velocity.cache&#x3D;false</span><br></pre></td></tr></table></figure>
<h3 id="基于Docker部署"><a href="#基于Docker部署" class="headerlink" title="基于Docker部署"></a>基于Docker部署</h3><hr>
<p>&nbsp;&nbsp;目前主流的PaaS平台都支持发布Docker镜像.而Docker镜像是使用Dockerfile文件来编译镜像的.</p>
<h4 id="Dockerfile"><a href="#Dockerfile" class="headerlink" title="Dockerfile"></a>Dockerfile</h4><hr>
<ol>
<li><p>FROM指令</p>
<p>FROM指令指明了当前镜像继承的基镜像.编译当前镜像时会自动下载基镜像.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">FROM java:8</span><br></pre></td></tr></table></figure>
</li>
<li><p>MAINTAINER指令</p>
<p>MAINTAINER指令指明了当前镜像的作者</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MAINTAINER sun</span><br></pre></td></tr></table></figure>
</li>
<li><p>RUN指令</p>
<p>RUN指令可以在当前镜像上执行Linux命令并形成一个新的层.RUN指令是编译时(build)的动作.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">RUN &#x2F;bin&#x2F;bash -c &quot;echo helloworld&quot;</span><br></pre></td></tr></table></figure></li>
<li><p>CMD指令</p>
<p>CMD指令指明镜像启动时的默认行为.一个Dockerfile里只能有一个CMD指令.CMD指令设定的命令可以在运行镜像时使用参数覆盖.它是运行时(run)的动作.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">CMD echo &quot;hello&quot;</span><br><span class="line">可被 docker run -d image_name echo &quot;world&quot; 覆盖.</span><br></pre></td></tr></table></figure>
</li>
<li><p>EXPOSE指令</p>
<p>EXPOSE指令指明了镜像运行时的容器必须监听指定的端口.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">EXPOSE 8080</span><br></pre></td></tr></table></figure>
</li>
<li><p>EVN指令</p>
<p>EVN指令用于设置环境变量.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ENV myName&#x3D;sun</span><br></pre></td></tr></table></figure>
</li>
<li><p>ADD指令</p>
<p>ADD指令用于从当前工作目录复制文件到镜像目录.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ADD hello.sh &#x2F;dir&#x2F;</span><br></pre></td></tr></table></figure>
</li>
<li><p>ENTRYPOINT指令</p>
<p>ENTRYPOINT指令可以让容器像一个可执行程序一样运行,这样镜像运行时可以像软件一样接收参数执行,ENTRYPOINT是运行时(run)的动作.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ENTRYPOINT [&quot;&#x2F;bin&#x2F;echo&quot;]</span><br><span class="line">我们可以镜像传递参数:</span><br><span class="line">docker run -d image_name &quot;hello world&quot;</span><br></pre></td></tr></table></figure>

</li>
</ol>
<h4 id="Example"><a href="#Example" class="headerlink" title="Example"></a>Example</h4><hr>
<ol>
<li><p>首先将打好包的demo上传到Linux服务器.</p>
</li>
<li><p>在项目同级目录下新建一个Dockerfile文件,如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">FROM java:8</span><br><span class="line"></span><br><span class="line">MAINTAINER&#x3D;sun</span><br><span class="line"></span><br><span class="line">ADD spring-boot-demo.jar app.jar</span><br><span class="line"></span><br><span class="line">EXPOSE 8080</span><br><span class="line"></span><br><span class="line">ENTRYPOINT [&quot;java&quot;,&quot;-jar&quot;,&quot;&#x2F;app.jar&quot;]</span><br></pre></td></tr></table></figure></li>
<li><p>编译镜像</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">在&#x2F;var&#x2F;apps&#x2F;sdemo目录下执行以下命令</span><br><span class="line">docker build -t sun&#x2F;sdemo .</span><br><span class="line">使用sun&#x2F;sdemo作为镜像名称,sun为前缀.  </span><br><span class="line">.是用来指明Dockerfile路径的,这里因为在当前目录下所以使用“.”.</span><br></pre></td></tr></table></figure>
</li>
<li><p>运行镜像</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run -d -p 8080:8080 --name sdemo sun&#x2F;sdemo</span><br></pre></td></tr></table></figure>
<h3 id="Spring-Boot集成测试"><a href="#Spring-Boot集成测试" class="headerlink" title="Spring Boot集成测试"></a>Spring Boot集成测试</h3></li>
</ol>
<hr>
<p>&nbsp;&nbsp;Spring Boot的测试与Spring MVC类似,它提供了一个<code>@SpringApplicationConfiguration</code>替代<code>@ContextConfiguration</code>来指定配置类.</p>
<h4 id="Example-1"><a href="#Example-1" class="headerlink" title="Example"></a>Example</h4><hr>
<ol>
<li><p>Entity</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Entity</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="meta">@GeneratedValue</span></span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>Dao</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">PersonRepositroy</span> <span class="keyword">extends</span> <span class="title">JpaRepository</span>&lt;<span class="title">Person</span>,<span class="title">Long</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>Controller</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/person&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">PersonController</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    PersonRepositroy personRepositroy;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequestMapping(method = RequestMethod.GET,produces = &#123;MediaType.APPLICATION_JSON_VALUE&#125;)</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> List&lt;Person&gt; <span class="title">findAll</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> personRepositroy.findAll();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>Test</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RunWith(SpringJUnit4ClassRunner.class)</span></span><br><span class="line"><span class="comment">//@ContextConfiguration(classes = &#123;SpringBootTestApplication.class&#125;)</span></span><br><span class="line"><span class="meta">@SpringApplicationConfiguration(classes = SpringBootTestApplication.class)</span></span><br><span class="line"><span class="meta">@WebAppConfiguration</span></span><br><span class="line"><span class="meta">@Transactional</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">PersonApplicationTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    PersonRepositroy personRepositroy;</span><br><span class="line"></span><br><span class="line">    MockMvc mockMvc;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    WebApplicationContext webApplicationContext;</span><br><span class="line"></span><br><span class="line">    String expectedJson;</span><br><span class="line"></span><br><span class="line">     <span class="comment">/**</span></span><br><span class="line"><span class="comment">       * 初始化</span></span><br><span class="line"><span class="comment">       */</span></span><br><span class="line">     <span class="meta">@Before</span></span><br><span class="line">     <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setUp</span><span class="params">()</span> <span class="keyword">throws</span> JsonProcessingException </span>&#123;</span><br><span class="line">         Person p1 = <span class="keyword">new</span> Person(<span class="string">&quot;sun&quot;</span>);</span><br><span class="line">         Person p2 = <span class="keyword">new</span> Person(<span class="string">&quot;sylvanas&quot;</span>);</span><br><span class="line">         personRepositroy.save(p1);</span><br><span class="line">         personRepositroy.save(p2);</span><br><span class="line">         <span class="comment">// 获得期待返回的JSON字符串.</span></span><br><span class="line">         expectedJson = Obj2Json(personRepositroy.findAll());</span><br><span class="line">         <span class="comment">// 初始化MockMvc.</span></span><br><span class="line">         mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();</span><br><span class="line">     &#125;</span><br><span class="line"></span><br><span class="line">     <span class="function"><span class="keyword">protected</span> String <span class="title">Obj2Json</span><span class="params">(Object obj)</span> <span class="keyword">throws</span> JsonProcessingException </span>&#123;</span><br><span class="line">         ObjectMapper objectMapper = <span class="keyword">new</span> ObjectMapper();</span><br><span class="line">         <span class="keyword">return</span> objectMapper.writeValueAsString(obj);</span><br><span class="line">     &#125;</span><br><span class="line"></span><br><span class="line">     <span class="meta">@Test</span></span><br><span class="line">     <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testPersonController</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">         String url = <span class="string">&quot;/person&quot;</span>;</span><br><span class="line">         <span class="comment">// 对/person发送请求,获得请求的执行结果.</span></span><br><span class="line">         MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get(url)</span><br><span class="line">                 .accept(MediaType.APPLICATION_JSON_VALUE))</span><br><span class="line">                 .andReturn();</span><br><span class="line">         <span class="comment">// 获得执行结果的状态</span></span><br><span class="line">         <span class="keyword">int</span> status = result.getResponse().getStatus();</span><br><span class="line">         <span class="comment">// 获得执行结果的内容</span></span><br><span class="line">         String content = result.getResponse().getContentAsString();</span><br><span class="line">         <span class="comment">// 断言</span></span><br><span class="line">         Assert.assertEquals(<span class="string">&quot;错误:正确的返回值为200&quot;</span>, <span class="number">200</span>, status);</span><br><span class="line">         Assert.assertEquals(<span class="string">&quot;错误:返回值和预期返回值不一致&quot;</span>, expectedJson, content);</span><br><span class="line">     &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="Spring-Boot应用监控"><a href="#Spring-Boot应用监控" class="headerlink" title="Spring Boot应用监控"></a>Spring Boot应用监控</h3></li>
</ol>
<hr>
<p>&nbsp;&nbsp;Spring Boot提供了运行时的应用监控和管理的功能.我们可以通过HTTP、JMX、SSH来进行应用的监控.</p>
<h4 id="HTTP"><a href="#HTTP" class="headerlink" title="HTTP"></a>HTTP</h4><hr>
<p>&nbsp;&nbsp;使用HTTP实现对应用的监控需要添加以下依赖:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&lt;dependency&gt;</span><br><span class="line">    &lt;groupId&gt;org.springframework.boot&lt;&#x2F;groupId&gt;</span><br><span class="line">    &lt;artifactId&gt;spring-boot-starter-actuator&lt;&#x2F;artifactId&gt;</span><br><span class="line">&lt;&#x2F;dependency&gt;</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;<strong>端点:</strong></p>
<table>
<thead>
<tr>
<th>Endpoint</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td>actuator</td>
<td>所有EndPoint的列表,需要加入spring HATEOAS支持</td>
</tr>
<tr>
<td>autoconfig</td>
<td>当前应用的所有自动配置</td>
</tr>
<tr>
<td>beans</td>
<td>当前应用的所有Bean信息</td>
</tr>
<tr>
<td>configprops</td>
<td>当前应用中所有的配置信息</td>
</tr>
<tr>
<td>dump</td>
<td>显示当前应用线程状态信息</td>
</tr>
<tr>
<td>env</td>
<td>显示当前应用的环境信息</td>
</tr>
<tr>
<td>health</td>
<td>显示当前应用的健康情况</td>
</tr>
<tr>
<td>info</td>
<td>显示当前应用信息</td>
</tr>
<tr>
<td>metrics</td>
<td>显示当前应用的各项指标信息</td>
</tr>
<tr>
<td>mappings</td>
<td>显示所有的@RequestMapping映射的路径</td>
</tr>
<tr>
<td>shutdown</td>
<td>关闭当前应用(默认此项是关闭的)</td>
</tr>
<tr>
<td>trace</td>
<td>默认最新的http请求</td>
</tr>
</tbody></table>
<p>&nbsp;&nbsp;使用http访问应用监控只需要在url中加上/端点名即可.</p>
<ul>
<li>例如: <a target="_blank" rel="noopener" href="http://localhost:8080/actuator">http://localhost:8080/actuator</a></li>
</ul>
<p>&nbsp;&nbsp;端点的开启关闭可以在application.properties中配置.</p>
<p>&nbsp;&nbsp;如果想自定义端点,则需要一个继承AbstractEndpoint的实现类,并注册成Bean即可.</p>
<p>&nbsp;&nbsp;如果想自定义HealthIndicator,则需要一个实现HealthIndicator接口的类,并注册成Bean即可.</p>
<h3 id="JMX"><a href="#JMX" class="headerlink" title="JMX"></a>JMX</h3><hr>
<p>&nbsp;&nbsp;可以使用Java内置的jconsole来实现JMX监控.</p>
<h3 id="SSH"><a href="#SSH" class="headerlink" title="SSH"></a>SSH</h3><hr>
<p>&nbsp;&nbsp;Spring Boot借助CraSH (<a target="_blank" rel="noopener" href="http://www.crashub.org/">http://www.crashub.org</a>), 实现通过SSH或者TELNET监控和管理应用.我们只需要引入依赖spring-boot-starter-remote-shell即可.</p>
<p>&nbsp;&nbsp;Spring Boot在spring-boot-starter-remote-shell的commands包下定制了命令,它使用的是Groovy语言来编写的.Groovy语言是由Spring主导的运行于JVM的动态语言.</p>
<h4 id="自定义登录用户"><a href="#自定义登录用户" class="headerlink" title="自定义登录用户"></a>自定义登录用户</h4><hr>
<p>&nbsp;&nbsp;Spring Boot支持在application.properties中自定义用户的账号密码.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">shell.auth.simple.user.name&#x3D;sun</span><br><span class="line">shell.auth.simple.user.password&#x3D;sun</span><br></pre></td></tr></table></figure>
<h3 id="end"><a href="#end" class="headerlink" title="end"></a>end</h3><blockquote>
<p>资料参考于 JavaEE开发的颠覆者: Spring Boot实战</p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2016/08/04/2016-08-4-Spring-batch/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2016/08/04/2016-08-4-Spring-batch/" class="post-title-link" itemprop="url">Spring Batch数据批处理</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2016-08-04 18:00:00" itemprop="dateCreated datePublished" datetime="2016-08-04T18:00:00+08:00">2016-08-04</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">后端</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/Spring-Boot/" itemprop="url" rel="index"><span itemprop="name">Spring Boot</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><hr>
<p>&nbsp;&nbsp;Spring Batch是一个轻量级的处理大量数据操作的框架,主要用于读取大量数据,然后进行一定处理后输出成指定的格式.</p>
<p>&nbsp;&nbsp;Spring Batch 提供了大量可重用的组件,包括了日志、追踪、事务、任务作业统计、任务重启、跳过、重复、资源管理。对于大数据量和高性能的批处理任务,Spring Batch 同样提供了高级功能和特性来支持,比如分区功能、远程功能。总之,通过 Spring Batch 能够支持简单的、复杂的和大数据量的批处理作业.</p>
<p>&nbsp;&nbsp;Spring Batch 是一个批处理应用框架,不是调度框架,但需要和调度框架合作来构建完成的批处理任务。它只关注批处理任务相关的问题,如事务、并发、监控、执行等,并不ﰁ供相应的调度功能。如果需要使用调用框架,在商业软件和开源软件中已经有很多优秀的企业级调度框架(如 Quartz、Tivoli、Control-M、Cron 等)可以使用.</p>
<h3 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h3><hr>
<ul>
<li>周期性的提交处理.</li>
<li>并行处理任务.</li>
<li>消息驱动应用分级处理.</li>
<li>手工或调度使任务失败之后重新启动.</li>
<li>有依赖步骤的顺序执行(使用工作流驱动扩展).</li>
<li>处理时跳过部分记录.</li>
<li>成批事务：为小批量的或有的存储过程/脚本的场景使用.</li>
</ul>
<h3 id="组成结构"><a href="#组成结构" class="headerlink" title="组成结构"></a>组成结构</h3><hr>
<p><img src="http://ww3.sinaimg.cn/mw690/63503acbjw1f6navcbgzhj20ki0860tg.jpg"></p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td>Job</td>
<td>实际要执行的任务,包含一个或多个Step</td>
</tr>
<tr>
<td>JobRepository</td>
<td>用于注册Job的容器</td>
</tr>
<tr>
<td>JobLauncher</td>
<td>用于启动Job的接口</td>
</tr>
<tr>
<td>Step</td>
<td>Step步骤包含ItemReader,ItemProcessor,ItemWriter</td>
</tr>
<tr>
<td>ItemReader</td>
<td>用于读取数据的接口</td>
</tr>
<tr>
<td>ItemProcessor</td>
<td>用于处理数据的接口</td>
</tr>
<tr>
<td>ItemWriter</td>
<td>用于输出数据的接口</td>
</tr>
</tbody></table>
<p><strong>&nbsp;&nbsp;以上组件只需要注册成Spring的Bean即可,并在配置类上使用<code>@EnableBatchProcessing</code>开启批处理的支持.</strong></p>
<h3 id="Job-Listener"><a href="#Job-Listener" class="headerlink" title="Job Listener"></a>Job Listener</h3><hr>
<p>&nbsp;&nbsp;如果想要监听Job的执行情况,则需要定义一个实现了<strong>JobExecutionListener</strong>的类,并在定义Job的Bean上绑定该监听器.</p>
<p><img src="http://ww1.sinaimg.cn/mw690/63503acbjw1f6ncme1ztdj20kk0e0n09.jpg"></p>
<p><img src="http://ww1.sinaimg.cn/mw690/63503acbjw1f6ncmeoja5j20c4054wey.jpg"></p>
<p><img src="http://ww3.sinaimg.cn/mw690/63503acbjw1f6ncmeln1gj20he07tjsq.jpg"></p>
<h3 id="数据处理和校验"><a href="#数据处理和校验" class="headerlink" title="数据处理和校验"></a>数据处理和校验</h3><hr>
<p>&nbsp;&nbsp;数据的处理和校验都要通过ItemProcessor接口实现来完成的.</p>
<h4 id="数据处理"><a href="#数据处理" class="headerlink" title="数据处理"></a>数据处理</h4><hr>
<p>&nbsp;&nbsp;实现ItemProcessor接口,并重写process方法,即可对数据进行处理.输入参数是从ItemReader读取到的数据,返回给ItemWriter.</p>
<p><img src="http://ww3.sinaimg.cn/mw690/63503acbjw1f6nd7a60nrj20kv06adh4.jpg"></p>
<h4 id="数据校验"><a href="#数据校验" class="headerlink" title="数据校验"></a>数据校验</h4><hr>
<p>&nbsp;&nbsp;数据校验可以使用JSR-303的注解来校验ItemReader读取的数据是否符合要求.</p>
<p><img src="http://ww1.sinaimg.cn/mw690/63503acbjw1f6ndeuabtvj20gt06ijs8.jpg" alt="定义实体类的检验规则"></p>
<p><img src="http://ww4.sinaimg.cn/mw690/63503acbjw1f6ndevin2gj20p30ku44i.jpg" alt="定义校验器"></p>
<p><img src="http://ww4.sinaimg.cn/mw690/63503acbjw1f6ndeuf8dkj20kc0c8acs.jpg" alt="ItemProcessor"></p>
<p><img src="http://ww4.sinaimg.cn/mw690/63503acbjw1f6ndev1nz8j20dk04s0t9.jpg" alt="注册校验器"></p>
<p><img src="http://ww3.sinaimg.cn/mw690/63503acbjw1f6ndeupgvxj20g407lgmy.jpg" alt="注册ItemProcessor"></p>
<h3 id="参数后置绑定"><a href="#参数后置绑定" class="headerlink" title="参数后置绑定"></a>参数后置绑定</h3><hr>
<p>&nbsp;&nbsp;实现参数后置绑定可以在JobParameters中绑定参数,在Bean定义的时候使用一个特殊的Bean生命周期注解<code>@StepScope</code>,然后通过<code>@Value</code>注入此参数.</p>
<p><img src="http://ww4.sinaimg.cn/mw690/63503acbjw1f6nejvlqcuj20hs0b3jtx.jpg" alt="绑定参数"></p>
<p><img src="http://ww3.sinaimg.cn/mw690/63503acbjw1f6nejv24v2j20u70e1n1d.jpg" alt="定义ItemReader"></p>
<h3 id="Spring-Boot支持"><a href="#Spring-Boot支持" class="headerlink" title="Spring Boot支持"></a>Spring Boot支持</h3><hr>
<p>&nbsp;&nbsp;Spring Boot自动初始化了Spring Batch存储批处理的数据库,并当程序启动时,会自动执行Job.</p>
<p>&nbsp;&nbsp;Spring Boot使用以<code>spring.batch</code>为前缀的属性进行相关配置.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">spring.batch.job.name&#x3D;job1,job2 #启动时要执行的job,默认执行全部job.</span><br><span class="line">spring.batch.job.enabled&#x3D;true #是否自动执行定义的job,默认为true</span><br><span class="line">spring.batch.initializer.enabled&#x3D;true #是否初始化Spring Batch的数据库,默认为true.</span><br><span class="line">spring.batch.schema&#x3D;</span><br><span class="line">spring.batch.table-prefix&#x3D; #设置Spring Batch的数据库表的前缀.</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;Spring Batch默认自动加载hsqldb驱动,如要使用其他数据库驱动则需要手动去除.</p>
<p><img src="http://ww3.sinaimg.cn/mw690/63503acbjw1f6neue2xagj20lw0i3n25.jpg"></p>
<h4 id="Config-Example"><a href="#Config-Example" class="headerlink" title="Config Example"></a>Config Example</h4><p><img src="http://ww2.sinaimg.cn/mw690/63503acbjw1f6s3o0w6n1j20u60dtdjx.jpg"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 定义ItemProcessor</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> ItemProcessor&lt;Person, Person&gt; <span class="title">processor</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 使用自定义的ItemProcessor</span></span><br><span class="line">    CsvItemProcessor processor = <span class="keyword">new</span> CsvItemProcessor();</span><br><span class="line">    <span class="comment">// 指定校验器</span></span><br><span class="line">    processor.setValidator(csvBeanValidator());</span><br><span class="line">    <span class="keyword">return</span> processor;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 定义Validator</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Validator&lt;Person&gt; <span class="title">csvBeanValidator</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> CsvBeanValidator&lt;Person&gt;();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 定义ItemWriter</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> ItemWriter&lt;Person&gt; <span class="title">writer</span><span class="params">(DataSource dataSource)</span> </span>&#123;</span><br><span class="line">    JdbcBatchItemWriter&lt;Person&gt; writer = <span class="keyword">new</span> JdbcBatchItemWriter&lt;&gt;();</span><br><span class="line">    writer.setItemSqlParameterSourceProvider</span><br><span class="line">            (<span class="keyword">new</span> BeanPropertyItemSqlParameterSourceProvider&lt;Person&gt;());</span><br><span class="line">    <span class="comment">// 设置要执行批处理的sql语句</span></span><br><span class="line">    String sql = <span class="string">&quot;insert into person &quot;</span> + <span class="string">&quot;(id,name,age,nation,address)&quot;</span></span><br><span class="line">            + <span class="string">&quot;values(hibernate_sequence.nextval,:name,:age,:nation,:address)&quot;</span>;</span><br><span class="line">    writer.setSql(sql);</span><br><span class="line">    writer.setDataSource(dataSource);</span><br><span class="line">    <span class="keyword">return</span> writer;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 定义JobRepository</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> JobRepository <span class="title">jobRepository</span><span class="params">(DataSource dataSource,</span></span></span><br><span class="line"><span class="function"><span class="params">                                   PlatformTransactionManager transactionManager)</span></span></span><br><span class="line"><span class="function">        <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">    JobRepositoryFactoryBean jobRepositoryFactoryBean = <span class="keyword">new</span> JobRepositoryFactoryBean();</span><br><span class="line">    jobRepositoryFactoryBean.setDataSource(dataSource);</span><br><span class="line">    jobRepositoryFactoryBean.setTransactionManager(transactionManager);</span><br><span class="line">    jobRepositoryFactoryBean.setDatabaseType(<span class="string">&quot;mysql&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> jobRepositoryFactoryBean.getObject();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 定义JobLauncher</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> JobLauncher <span class="title">jobLauncher</span><span class="params">(DataSource dataSource,</span></span></span><br><span class="line"><span class="function"><span class="params">                               PlatformTransactionManager transactionManager)</span></span></span><br><span class="line"><span class="function">        <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">    SimpleJobLauncher jobLauncher = <span class="keyword">new</span> SimpleJobLauncher();</span><br><span class="line">    jobLauncher.setJobRepository(jobRepository(dataSource, transactionManager));</span><br><span class="line">    <span class="keyword">return</span> jobLauncher;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 定义Job</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Job <span class="title">importJob</span><span class="params">(JobBuilderFactory jobs, Step s1)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> jobs.get(<span class="string">&quot;importJob&quot;</span>)</span><br><span class="line">            .incrementer(<span class="keyword">new</span> RunIdIncrementer())</span><br><span class="line">            .flow(s1) <span class="comment">// 为job指定step</span></span><br><span class="line">            .end()</span><br><span class="line">            .listener(csvJobListener()) <span class="comment">// 绑定监听器</span></span><br><span class="line">            .build();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 定义Step</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Step <span class="title">step1</span><span class="params">(StepBuilderFactory stepBuilderFactory, ItemReader&lt;Person&gt; reader,</span></span></span><br><span class="line"><span class="function"><span class="params">                  ItemWriter&lt;Person&gt; writer,</span></span></span><br><span class="line"><span class="function"><span class="params">                  ItemProcessor&lt;Person, Person&gt; processor)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> stepBuilderFactory</span><br><span class="line">            .get(<span class="string">&quot;step1&quot;</span>)</span><br><span class="line">            .&lt;Person, Person&gt;chunk(<span class="number">65000</span>) <span class="comment">// 批处理每次提交65000条数据.</span></span><br><span class="line">            .reader(reader)</span><br><span class="line">            .processor(processor)</span><br><span class="line">            .writer(writer)</span><br><span class="line">            .build();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 定义自定义的Job监听器</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> CsvJobListener <span class="title">csvJobListener</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> CsvJobListener();</span><br><span class="line">&#125;</span><br><span class="line">	</span><br></pre></td></tr></table></figure>
<h3 id="end"><a href="#end" class="headerlink" title="end"></a>end</h3><blockquote>
<p>资料参考于 JavaEE开发的颠覆者: Spring Boot实战</p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2016/08/03/2016-08-3-Spring-message/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2016/08/03/2016-08-3-Spring-message/" class="post-title-link" itemprop="url">Spring Boot消息通信</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2016-08-03 18:00:00" itemprop="dateCreated datePublished" datetime="2016-08-03T18:00:00+08:00">2016-08-03</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">后端</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/Spring-Boot/" itemprop="url" rel="index"><span itemprop="name">Spring Boot</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="SSL安全套接层"><a href="#SSL安全套接层" class="headerlink" title="SSL安全套接层"></a>SSL安全套接层</h3><hr>
<h4 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h4><hr>
<p>&nbsp;&nbsp;SSL(Secure Sockets Layer)是专门用于网络通信安全及数据完整性的安全协议,它会在网络传输层对网络连接进行加密.</p>
<p>&nbsp;&nbsp;SSL协议是位于TCP/IP协议与其他各种应用层协议之间的.</p>
<p>&nbsp;&nbsp;SSL的体系结构中包含两个协议子层:</p>
<ol>
<li><p>SSL记录协议(SSL Record Protocol),它建立在可靠的传输协议(TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持.SSL纪录协议针对HTTP协议进行了特别的设计，使得超文本的传输协议HTTP能够在SSL运行.</p>
</li>
<li><p>SSL握手协议(SSL Handshake Protocol),它建立在SSL记录协议之上.SSL握手协议层包括SSL握手协议（SSL HandShake Protocol）、SSL密码参数修改协议（SSL Change Cipher Spec Protocol）、应用数据协议（Application Data Protocol）和SSL告警协议（SSL Alert Protocol）.它主要用于在实际数据传输开始前,通信双方进行身份认证、协商加密算法、交换加密密钥等.</p>
<p>&nbsp;&nbsp;在B/S应用中,是通过<strong>HTTPS</strong>实现SSL的,<strong>HTTPS</strong>即是在HTTP下加入SSL层,它的安全基础是SSL.</p>
</li>
</ol>
<h4 id="工作流程"><a href="#工作流程" class="headerlink" title="工作流程"></a>工作流程</h4><hr>
<ol>
<li>客户端向服务器发送一个开始信息“Hello”以便开始一个新的会话连接.</li>
<li>服务器根据客户的信息确定是否需要生成新的主密钥，如需要则服务器在响应客户的“Hello”信息时将包含生成主密钥所需的信息.</li>
<li>客户根据收到的服务器响应信息，产生一个主密钥，并用服务器的公开密钥加密后传给服务器.</li>
<li>服务器回复该主密钥，并返回给客户一个用主密钥认证的信息，以此让客户认证服务器.</li>
</ol>
<h4 id="Spring-Boot中配置SSL"><a href="#Spring-Boot中配置SSL" class="headerlink" title="Spring Boot中配置SSL"></a>Spring Boot中配置SSL</h4><hr>
<p>&nbsp;&nbsp;在配置SSL之前需要生成一个证书,它可以是自签名的,也可以是从SSL证书授权中心获得的.</p>
<h5 id="生成SSL证书"><a href="#生成SSL证书" class="headerlink" title="生成SSL证书"></a>生成SSL证书</h5><p>&nbsp;&nbsp;可以在控制台中输入以下指令,生成自签名的SSL证书:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">keytool -genkey -alias tomcat</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;之后会在当前目录生成一个.keystore文件,它就是SSL证书文件.</p>
<h5 id="配置到项目中"><a href="#配置到项目中" class="headerlink" title="配置到项目中"></a>配置到项目中</h5><ol>
<li>将.keystore文件复制到src/main/resources/static下.</li>
<li>在application.properties中如下配置:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">server.port &#x3D; 8090</span><br><span class="line">server.ssl.key-store &#x3D; .keystore</span><br><span class="line">server.ssl.key-store-password &#x3D; 123456</span><br><span class="line">server.ssl.keyStoreType &#x3D; JKS</span><br><span class="line">server.ssl.keyAlias : tomcat</span><br></pre></td></tr></table></figure>
<h4 id="http自动转向https"><a href="#http自动转向https" class="headerlink" title="http自动转向https"></a>http自动转向https</h4></li>
</ol>
<hr>
<p>&nbsp;&nbsp;如果想要实现输入http自动转向到https,则需要配置<strong>TomcatEmbeddedServletContainerFactory</strong>,并添加Tomcat的connector来实现.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.sun.sylvanas.spring_boot_security;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.apache.catalina.Context;</span><br><span class="line"><span class="keyword">import</span> org.apache.catalina.connector.Connector;</span><br><span class="line"><span class="keyword">import</span> org.apache.tomcat.util.descriptor.web.SecurityCollection;</span><br><span class="line"><span class="keyword">import</span> org.apache.tomcat.util.descriptor.web.SecurityConstraint;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.SpringApplication;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.autoconfigure.SpringBootApplication;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Bean;</span><br><span class="line"></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SpringBootSecurityApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">		SpringApplication.run(SpringBootSecurityApplication.class, args);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Bean</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> EmbeddedServletContainerFactory <span class="title">servletContainerFactory</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		TomcatEmbeddedServletContainerFactory tomcat = <span class="keyword">new</span> TomcatEmbeddedServletContainerFactory()&#123;</span><br><span class="line">			<span class="meta">@Override</span></span><br><span class="line">			<span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">postProcessContext</span><span class="params">(Context context)</span> </span>&#123;</span><br><span class="line">				SecurityConstraint securityConstraint = <span class="keyword">new</span> SecurityConstraint();</span><br><span class="line">				securityConstraint.setUserConstraint(<span class="string">&quot;CONFIDENTIAL&quot;</span>);</span><br><span class="line">				SecurityCollection securityCollection = <span class="keyword">new</span> SecurityCollection();</span><br><span class="line">				securityCollection.addPattern(<span class="string">&quot;/&quot;</span>);</span><br><span class="line">				securityConstraint.addCollection(securityCollection);</span><br><span class="line">				context.addConstraint(securityConstraint);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;;</span><br><span class="line">		tomcat.addAdditionalTomcatConnectors(httpConnector());</span><br><span class="line">		<span class="keyword">return</span> tomcat;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Bean</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> Connector <span class="title">httpConnector</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		Connector connector = <span class="keyword">new</span> Connector(<span class="string">&quot;org.apache.coyote.http11.Http11NioProtocol&quot;</span>);</span><br><span class="line">		connector.setScheme(<span class="string">&quot;http&quot;</span>);</span><br><span class="line">		connector.setPort(<span class="number">8080</span>);</span><br><span class="line">		connector.setSecure(<span class="keyword">false</span>);</span><br><span class="line">		connector.setRedirectPort(<span class="number">8090</span>);</span><br><span class="line">		<span class="keyword">return</span> connector;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;此时访问<a href="http://localhost:8080会自动转向到https://localhost:8090">http://localhost:8080会自动转向到https://localhost:8090</a>.</p>
<h3 id="WebSocket"><a href="#WebSocket" class="headerlink" title="WebSocket"></a>WebSocket</h3><hr>
<h4 id="概述-1"><a href="#概述-1" class="headerlink" title="概述"></a>概述</h4><hr>
<p><img src="http://ww2.sinaimg.cn/mw690/63503acbjw1f6mihqw3jpj20sg0dttc2.jpg"><br>&nbsp;&nbsp;WebSocket protocol 是HTML5一种新的协议.它实现了浏览器与服务器全双工异步通信(full-duplex).即浏览器可以向服务端发送消息,服务端也可以向浏览器发送消息.WebSocket需要浏览器的支持,如IE 10+、Chrome 13+、Firefox 6+.</p>
<p>&nbsp;&nbsp;WebSocket是通过一个socket来实现双工异步通信功能的.直接使用WebSocket协议开发程序会很繁琐,所以一般使用它的子协议<strong>STOMP</strong>,<strong>STOMP</strong>是一个更高级别的协议,它使用基于帧(frame)的格式来定义消息,具有一个类似<code>@RequestMapping</code>的<code>@MessageMapping</code>.</p>
<h4 id="Spring-Boot支持"><a href="#Spring-Boot支持" class="headerlink" title="Spring Boot支持"></a>Spring Boot支持</h4><hr>
<p>&nbsp;&nbsp;Spring Boot对内嵌的Tomcat(7、8)、Jetty9、Undertow提供了WebSocket支持.依赖为spring-boot-starter-websocket.</p>
<p>&nbsp;&nbsp;配置WebSocket需要在配置类上使用<code>@EnableWebSocketMessageBroker</code>注解开启支持,并且继承<strong>AbstractWebSocketMessageBrokerConfigurer</strong>类,重写其方法进行配置.</p>
<h4 id="Example"><a href="#Example" class="headerlink" title="Example"></a>Example</h4><hr>
<h5 id="广播"><a href="#广播" class="headerlink" title="广播"></a>广播</h5><p>&nbsp;&nbsp;广播即服务端有消息时,会将消息发送给所有连接了当前endpoint的浏览器.</p>
<ol>
<li><p>配置</p>
<p><img src="http://ww1.sinaimg.cn/mw690/63503acbjw1f6mihr9qtsj20om0ddadp.jpg"></p>
</li>
<li><p>Controller</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@MessageMapping</span>注解映射/hello这个地址,类似于<span class="doctag">@RequestMapping</span></span></span><br><span class="line"><span class="comment">  * 当服务端有消息时,会对订阅了<span class="doctag">@SendTo</span>中的路径的浏览器发送消息.</span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line"> <span class="meta">@MessageMapping(&quot;/hello&quot;)</span></span><br><span class="line"> <span class="meta">@SendTo(&quot;/topic/getHello&quot;)</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">hello</span><span class="params">(String name)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">     Thread.sleep(<span class="number">3000</span>);</span><br><span class="line">     <span class="keyword">return</span> <span class="string">&quot;Hello &quot;</span> + name + <span class="string">&quot;!&quot;</span>;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>js</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line">  &lt;html xmlns:th=<span class="string">&quot;http://www.thymeleaf.org&quot;</span>&gt;</span><br><span class="line"></span><br><span class="line">&lt;head&gt;</span><br><span class="line">    &lt;meta content=<span class="string">&quot;text/html;charset=UTF-8&quot;</span>/&gt;</span><br><span class="line">    &lt;meta http-equiv=<span class="string">&quot;X-UA-Compatible&quot;</span> content=<span class="string">&quot;IE=edge&quot;</span>/&gt;</span><br><span class="line">    &lt;meta name=<span class="string">&quot;viewport&quot;</span> content=<span class="string">&quot;width=device-width,initial-scale=1&quot;</span>/&gt;</span><br><span class="line">    &lt;title&gt;Spring Boot+WebSocket+广播式&lt;/title&gt;</span><br><span class="line">&lt;/head&gt;</span><br><span class="line">&lt;body onload=<span class="string">&quot;disconnect()&quot;</span>&gt;</span><br><span class="line">&lt;noscript&gt;<span class="xml"><span class="tag">&lt;<span class="name">h2</span> <span class="attr">style</span>=<span class="string">&quot;color: #ff0000&quot;</span>&gt;</span>貌似你的浏览器不支持websocket<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span>&lt;/noscript&gt;</span><br><span class="line">&lt;div&gt;</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">        &lt;button id=<span class="string">&quot;connect&quot;</span> onclick=<span class="string">&quot;connect();&quot;</span>&gt;连接&lt;/button&gt;</span><br><span class="line">        &lt;button id=<span class="string">&quot;disconnect&quot;</span> onclick=<span class="string">&quot;disconnect();&quot;</span>&gt;断开连接&lt;/button&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">    &lt;div id=<span class="string">&quot;conversationDiv&quot;</span>&gt;</span><br><span class="line">        &lt;label&gt;输入你的名字&lt;<span class="regexp">/label&gt;&lt;input type=&quot;text&quot; id=&quot;name&quot;/</span>&gt;</span><br><span class="line">        &lt;button id=<span class="string">&quot;sendName&quot;</span> onclick=<span class="string">&quot;sendName();&quot;</span>&gt;发送&lt;/button&gt;</span><br><span class="line">        &lt;p id=<span class="string">&quot;response&quot;</span>&gt;&lt;/p&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">&lt;/div&gt;</span><br><span class="line">&lt;!-- 导入jQuery --&gt;</span><br><span class="line">&lt;script th:src=<span class="string">&quot;@&#123;jquery-3.1.0.min.js&#125;&quot;</span>/&gt;</span><br><span class="line">&lt;script th:src=<span class="string">&quot;@&#123;sockjs-1.0.0.min.js&#125;&quot;</span>/&gt;</span><br><span class="line">&lt;script th:src=<span class="string">&quot;@&#123;stomp.min.js&#125;&quot;</span>/&gt;</span><br><span class="line">&lt;script type=<span class="string">&quot;text/javascript&quot;</span>&gt;</span><br><span class="line">    <span class="keyword">var</span> stompClient = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">setConnected</span>(<span class="params">connected</span>) </span>&#123;</span><br><span class="line">        <span class="built_in">document</span>.getElementById(<span class="string">&#x27;connect&#x27;</span>).disabled = connected;</span><br><span class="line">        <span class="built_in">document</span>.getElementById(<span class="string">&#x27;disconnect&#x27;</span>).disabled = !connected;</span><br><span class="line">        <span class="built_in">document</span>.getElementById(<span class="string">&#x27;conversationDiv&#x27;</span>).style.visibility = connected ? <span class="string">&#x27;visible&#x27;</span> : <span class="string">&#x27;hidden&#x27;</span>;</span><br><span class="line">        $(<span class="string">&#x27;#response&#x27;</span>).html();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">connect</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> socket = <span class="keyword">new</span> SockJS(<span class="string">&#x27;/endpointSun&#x27;</span>);</span><br><span class="line">        stompClient = Stomp.over(socket);</span><br><span class="line">        stompClient.connect(&#123;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params">frame</span>) </span>&#123;</span><br><span class="line">            setConnected(<span class="literal">true</span>);</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">&#x27;Connected: &#x27;</span> + frame);</span><br><span class="line">            <span class="comment">// 订阅/topic/getHello</span></span><br><span class="line">            stompClient.subscribe(<span class="string">&#x27;/topic/getHello&#x27;</span>,<span class="function"><span class="keyword">function</span>(<span class="params">response</span>)</span>&#123;</span><br><span class="line">                showResponse(<span class="built_in">JSON</span>.parse(response.body).responseMessage);</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">disconnect</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(stompClient != <span class="literal">null</span>)&#123;</span><br><span class="line">            stompClient.disconnect();</span><br><span class="line">        &#125;</span><br><span class="line">        setConnected(<span class="literal">false</span>);</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&quot;Disconnected&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">sendName</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> name = $(<span class="string">&#x27;#name&#x27;</span>).val();</span><br><span class="line">        <span class="comment">// 发送到/hello</span></span><br><span class="line">        stompClient.send(<span class="string">&quot;/hello&quot;</span>,&#123;&#125;,<span class="built_in">JSON</span>.stringify(&#123;<span class="string">&#x27;name&#x27;</span>:name&#125;));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">showResponse</span>(<span class="params">message</span>)</span>&#123;</span><br><span class="line">        <span class="keyword">var</span> response = $(<span class="string">&quot;#response&quot;</span>);</span><br><span class="line">        response.html(message);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&lt;/script&gt;</span><br><span class="line">&lt;/body&gt;</span><br><span class="line"></span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure>
<h5 id="P2P"><a href="#P2P" class="headerlink" title="P2P"></a>P2P</h5></li>
</ol>
<p>&nbsp;&nbsp;P2P即点对点,它不同于广播式,P2P需要指定消息由谁接收,且只能由一个人接收.</p>
<ol>
<li><p>配置与广播式相同.</p>
</li>
<li><p>Controller</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">  * 通过SimpMessagingTemplate向浏览器发送消息.</span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> SimpMessagingTemplate messagingTemplate;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment">  * Spring MVC可以直接在参数中注入principal对象,它包含当前用户的信息.</span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line"> <span class="meta">@MessageMapping(&quot;/chat&quot;)</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleChat</span><span class="params">(Principal principal, String msg)</span> </span>&#123;</span><br><span class="line">     <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * 通过SimpMessagingTemplate.convertAndSendToUser向用户发送消息,</span></span><br><span class="line"><span class="comment">      * 第一个参数是接收者的用户,第二参数是浏览器订阅的地址,第三个参数是要发送的消息.</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">     <span class="keyword">if</span> (principal.getName().equals(<span class="string">&quot;sun&quot;</span>)) &#123;</span><br><span class="line">         messagingTemplate.convertAndSendToUser(<span class="string">&quot;sylvanas&quot;</span>,</span><br><span class="line">                 <span class="string">&quot;/queue/notifications&quot;</span>, principal.getName() + <span class="string">&quot;-send:&quot;</span> + msg);</span><br><span class="line">     &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">         messagingTemplate.convertAndSendToUser(<span class="string">&quot;sun&quot;</span>,</span><br><span class="line">                 <span class="string">&quot;/queue/notifications&quot;</span>, principal.getName() + <span class="string">&quot;-send:&quot;</span> + msg);</span><br><span class="line">     &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure></li>
<li><p>js</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line">  &lt;!DOCTYPE html&gt;</span><br><span class="line">&lt;!--suppress ALL --&gt;</span><br><span class="line">&lt;html xmlns=<span class="string">&quot;http://www.w3.org/1999/xhtml&quot;</span> xmlns:th=<span class="string">&quot;http://www.thymeleaf.org&quot;</span></span><br><span class="line">      xmlns:sec=<span class="string">&quot;http://www.thymeleaf.org/thymeleaf-extras-springsecurity3&quot;</span>&gt;</span><br><span class="line"></span><br><span class="line">&lt;head&gt;</span><br><span class="line">    &lt;meta content=<span class="string">&quot;text/html;charset=UTF-8&quot;</span>/&gt;</span><br><span class="line">    &lt;meta http-equiv=<span class="string">&quot;X-UA-Compatible&quot;</span> content=<span class="string">&quot;IE=edge&quot;</span>/&gt;</span><br><span class="line">    &lt;meta name=<span class="string">&quot;viewport&quot;</span> content=<span class="string">&quot;width=device-width,initial-scale=1&quot;</span>/&gt;</span><br><span class="line">    &lt;script th:src=<span class="string">&quot;@&#123;jquery-3.1.0.min.js&#125;&quot;</span>/&gt;</span><br><span class="line">    &lt;script th:src=<span class="string">&quot;@&#123;stomp.min.js&#125;&quot;</span>/&gt;</span><br><span class="line">    &lt;script th:src=<span class="string">&quot;@&#123;sockjs-1.0.0.min.js&#125;&quot;</span>/&gt;</span><br><span class="line">&lt;/head&gt;</span><br><span class="line">&lt;body&gt;</span><br><span class="line">&lt;p&gt;</span><br><span class="line">    聊天室</span><br><span class="line">&lt;/p&gt;</span><br><span class="line"></span><br><span class="line">&lt;form id=<span class="string">&quot;chatForm&quot;</span>&gt;</span><br><span class="line">    &lt;textarea rows=<span class="string">&quot;4&quot;</span> cols=<span class="string">&quot;60&quot;</span> name=<span class="string">&quot;text&quot;</span>&gt;&lt;/textarea&gt;</span><br><span class="line">    &lt;input type=<span class="string">&quot;submit&quot;</span>/&gt;</span><br><span class="line">&lt;/form&gt;</span><br><span class="line"></span><br><span class="line">&lt;script th:inline=<span class="string">&quot;javascript&quot;</span>&gt;</span><br><span class="line">    $(<span class="string">&#x27;#chatForm&#x27;</span>).submit(<span class="function"><span class="keyword">function</span>(<span class="params">e</span>)</span>&#123;</span><br><span class="line">        e.preventDefault();</span><br><span class="line">        <span class="keyword">var</span> text = $(<span class="string">&#x27;#chatForm&#x27;</span>).find(<span class="string">&#x27;textarea[name=&quot;text&quot;]&#x27;</span>).val();</span><br><span class="line">        sendSpittle(text);</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> socket = <span class="keyword">new</span> SockJS(<span class="string">&quot;/endpointChat&quot;</span>);</span><br><span class="line">    <span class="keyword">var</span> stomp = Stomp.over(socket);</span><br><span class="line">    stomp.connect(<span class="string">&#x27;guest&#x27;</span>,<span class="string">&#x27;guest&#x27;</span>,<span class="function"><span class="keyword">function</span>(<span class="params">frame</span>)</span>&#123;</span><br><span class="line">        <span class="comment">// 与messagingTemplate.convertAndSendToUser中定义的订阅地址保持一致</span></span><br><span class="line">        <span class="comment">// 但前面要多出一个/user,这个/user是必须的,只有使用了/user才会发送消息到指定的用户.</span></span><br><span class="line">        stomp.subscribe(<span class="string">&quot;/user/queue/notifications&quot;</span>,handleNotification);</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">handleNotification</span>(<span class="params">message</span>)</span>&#123;</span><br><span class="line">        $(<span class="string">&quot;#output&quot;</span>).append(<span class="string">&quot;&lt;b&gt;Received: &quot;</span> + message.body + <span class="string">&quot;&lt;/b&gt;&lt;br/&gt;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">sendSpittle</span>(<span class="params">text</span>)</span>&#123;</span><br><span class="line">        stomp.send(<span class="string">&quot;/chat&quot;</span>,&#123;&#125;,text);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    $(<span class="string">&#x27;#stop&#x27;</span>).click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;socket.close()&#125;);</span><br><span class="line"></span><br><span class="line">&lt;/script&gt;</span><br><span class="line"></span><br><span class="line">&lt;div id=<span class="string">&quot;output&quot;</span>&gt;&lt;/div&gt;</span><br><span class="line"></span><br><span class="line">&lt;/body&gt;</span><br><span class="line"></span><br><span class="line">&lt;/html&gt; </span><br></pre></td></tr></table></figure>
<h3 id="异步消息队列"><a href="#异步消息队列" class="headerlink" title="异步消息队列"></a>异步消息队列</h3></li>
</ol>
<hr>
<p>&nbsp;&nbsp;异步消息队列主要用于各系统之间的通信与解耦,异步消息即为发送者无需关心消息接收者的处理和返回.</p>
<p>&nbsp;&nbsp;异步消息的主要概念为消息代理(message broker)和目的地(destination).消息是由消息代理负责接管并传递到指定目的地的.</p>
<p>&nbsp;&nbsp;目的地主要有2种:</p>
<ol>
<li>queue:用于P2P(point-to-point)的消息通信.</li>
<li>topic:用于发布/订阅(publish/subscribe)的消息通信.</li>
</ol>
<h4 id="Spring-Boot支持-1"><a href="#Spring-Boot支持-1" class="headerlink" title="Spring Boot支持"></a>Spring Boot支持</h4><hr>
<p>&nbsp;&nbsp;Spring对JMS和AMQP的支持来自于spring-jms、spring-rabbit.它们分别需要ConnectionFactory的实现来连接消息代理.并且提供了JmsTemplate和RabbitTemplate.</p>
<ul>
<li>JMS(Java Message Service)Java消息服务,它是基于JVM消息代理的规范.</li>
<li>AMQP(Advanced Message Queuing Protocol)高级消息队列协议,它不仅支持JVM,还支持跨语言和平台.AMQP的主要实现有RabbitMQ.</li>
</ul>
<p>&nbsp;&nbsp;Spring Boot对JMS支持的实有ActiveMQ,HornetQ,Artemis.以ActiveMQ为例:</p>
<ul>
<li>Spring Boot自动配置了ActiveMQConnectionFactory与JmsTemplate.</li>
<li>通过<code>spring.activemq</code>为前缀的属性来配置ActiveMQ相关的属性.</li>
<li>Spring Boot还自动开启了<code>@EnableJms</code>,即使用注解式消息监听的支持.</li>
</ul>
<p>&nbsp;&nbsp;Spring Boot对AMQP的实现RabbitMQ自动配置了如下内容:</p>
<ul>
<li>自动配置了ConnectionFactory和RabbitTemplate.</li>
<li>通过<code>spring.rabbitmq</code>为前缀的属性来配置RabbitMQ相关的属性.</li>
<li>自动开启了<code>@EnableRabbit</code>,即使用注解式消息监听的支持.</li>
</ul>
<h4 id="JMS-Example"><a href="#JMS-Example" class="headerlink" title="JMS Example"></a>JMS Example</h4><hr>
<p>&nbsp;&nbsp;添加依赖</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">      &lt;dependency&gt;</span><br><span class="line">	&lt;groupId&gt;org.springframework.boot&lt;&#x2F;groupId&gt;</span><br><span class="line">	&lt;artifactId&gt;spring-boot-starter&lt;&#x2F;artifactId&gt;</span><br><span class="line">&lt;&#x2F;dependency&gt;</span><br><span class="line">&lt;dependency&gt;</span><br><span class="line">	&lt;groupId&gt;org.springframework&lt;&#x2F;groupId&gt;</span><br><span class="line">	&lt;artifactId&gt;spring-jms&lt;&#x2F;artifactId&gt;</span><br><span class="line">&lt;&#x2F;dependency&gt;</span><br><span class="line">&lt;dependency&gt;</span><br><span class="line">	&lt;groupId&gt;org.apache.activemq&lt;&#x2F;groupId&gt;</span><br><span class="line">	&lt;artifactId&gt;activemq-client&lt;&#x2F;artifactId&gt;</span><br><span class="line">&lt;&#x2F;dependency&gt;</span><br><span class="line"></span><br><span class="line">&lt;!-- 嵌入ActiveMQ --&gt;</span><br><span class="line">&lt;dependency&gt;</span><br><span class="line">	&lt;groupId&gt;org.apache.activemq&lt;&#x2F;groupId&gt;</span><br><span class="line">	&lt;artifactId&gt;activemq-broker&lt;&#x2F;artifactId&gt;</span><br><span class="line">&lt;&#x2F;dependency&gt;</span><br><span class="line"></span><br><span class="line">&lt;dependency&gt;</span><br><span class="line">	&lt;groupId&gt;org.springframework.boot&lt;&#x2F;groupId&gt;</span><br><span class="line">	&lt;artifactId&gt;spring-boot-starter-test&lt;&#x2F;artifactId&gt;</span><br><span class="line">	&lt;scope&gt;test&lt;&#x2F;scope&gt;</span><br><span class="line">&lt;&#x2F;dependency&gt;</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;在application.properties中配置activemq的地址.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">spring.activemq.broker-url&#x3D;tcp:&#x2F;&#x2F;192.168.145.152:61616</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;定义JMS发送的消息需要实现MessageCreator接口.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Msg</span> <span class="keyword">implements</span> <span class="title">MessageCreator</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 重写createMessage方法.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Message <span class="title">createMessage</span><span class="params">(Session session)</span> <span class="keyword">throws</span> JMSException </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> session.createTextMessage(<span class="string">&quot;Hello World&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;发送消息,定义目的地.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Spring Boot提供了一个CommandLineRunner接口,用于程序启动后执行的代码</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SuppressWarnings(&quot;SpringJavaAutowiringInspection&quot;)</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SpringBootActivemqApplication</span> <span class="keyword">implements</span> <span class="title">CommandLineRunner</span> </span>&#123;</span><br><span class="line">	</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">		SpringApplication.run(SpringBootActivemqApplication.class, args);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Autowired</span></span><br><span class="line">	<span class="keyword">private</span> JmsTemplate jmsTemplate;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">(String... strings)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		<span class="comment">// 向目的地my-destination发送消息</span></span><br><span class="line">		jmsTemplate.send(<span class="string">&quot;my-destination&quot;</span>,<span class="keyword">new</span> Msg());</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;接收消息</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Receiver</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@JmsListener</span>是Spring 4.1提供的一个新特性,用来简化JMS开发.</span></span><br><span class="line"><span class="comment">     * destination指定要监听的目的地.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@JmsListener(destination = &quot;my-destination&quot;)</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">receiveMessage</span><span class="params">(String message)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;接收到: &lt;&quot;</span> + message + <span class="string">&quot;&gt;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="AMQP-Example"><a href="#AMQP-Example" class="headerlink" title="AMQP Example"></a>AMQP Example</h4><hr>
<p>&nbsp;&nbsp;Spring Boot默认Rabbit主机位localhost,端口号为5672.</p>
<p>&nbsp;&nbsp;添加以下依赖:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">      &lt;dependency&gt;</span><br><span class="line">	&lt;groupId&gt;org.springframework.boot&lt;&#x2F;groupId&gt;</span><br><span class="line">	&lt;artifactId&gt;spring-boot-starter-amqp&lt;&#x2F;artifactId&gt;</span><br><span class="line">&lt;&#x2F;dependency&gt;</span><br><span class="line">&lt;dependency&gt;</span><br><span class="line">	&lt;groupId&gt;org.springframework.boot&lt;&#x2F;groupId&gt;</span><br><span class="line">	&lt;artifactId&gt;spring-boot-starter-test&lt;&#x2F;artifactId&gt;</span><br><span class="line">	&lt;scope&gt;test&lt;&#x2F;scope&gt;</span><br><span class="line">&lt;&#x2F;dependency&gt;</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;在application.properties中配置RabbitMQ的地址.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">spring.rabbitmq.host&#x3D;192.168.145.152</span><br><span class="line">spring.rabbitmq.port&#x3D;5672</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;发送消息,定义目的地.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SuppressWarnings(&quot;SpringJavaAutowiringInspection&quot;)</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SpringBootRabbitmqApplication</span> <span class="keyword">implements</span> <span class="title">CommandLineRunner</span> </span>&#123;</span><br><span class="line">	</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">		SpringApplication.run(SpringBootRabbitmqApplication.class, args);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Autowired</span></span><br><span class="line">	<span class="keyword">private</span> RabbitTemplate rabbitTemplate;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * 定义目的地</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">	<span class="meta">@Bean</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> Queue <span class="title">myQueue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">new</span> Queue(<span class="string">&quot;my-queue&quot;</span>);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * 使用rabbitTemplate的convertAndSend方法向队列my-queue发送消息.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">(String... strings)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		rabbitTemplate.convertAndSend(<span class="string">&quot;my-queue&quot;</span>,<span class="string">&quot;Hello RabbitMQ!&quot;</span>);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>&nbsp;&nbsp;接收消息</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Receiver</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RabbitListener(queues = &quot;my-queue&quot;)</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">receiveMessage</span><span class="params">(String message)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Received &lt;&quot;</span> + message + <span class="string">&quot;&gt;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="end"><a href="#end" class="headerlink" title="end"></a>end</h3><blockquote>
<p>资料参考于 JavaEE开发的颠覆者: Spring Boot实战</p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2016/08/02/2016-08-2-Spring-security/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2016/08/02/2016-08-2-Spring-security/" class="post-title-link" itemprop="url">Spring Security安全控制</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2016-08-02 18:00:00" itemprop="dateCreated datePublished" datetime="2016-08-02T18:00:00+08:00">2016-08-02</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">后端</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/Spring-Boot/" itemprop="url" rel="index"><span itemprop="name">Spring Boot</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><hr>
<p>&nbsp;&nbsp;Spring Security是Spring项目的安全框架,基于IoC和AOP来实现安全的功能.</p>
<p>&nbsp;&nbsp;Spring Security有两个重要概念:</p>
<ul>
<li>认证(Authentication),认证即确认用户可以访问当前系统.</li>
<li>授权(Authorization),授权即确定用户在当前系统下所拥有的权限.</li>
</ul>
<h3 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h3><hr>
<p>&nbsp;&nbsp;Spring Security使用过滤器来实现所有安全的功能,我们只需要注册一个特殊的<strong>DelegationFilterProxy</strong>过滤器到<strong>WebApplicationInitializer</strong>即可(WebApplicationInitializer是Spring提供用来配置Servlet3.0+配置的接口,从而替代web.xml,实现此接口的类会自动被SpringServletContainerInitializer加载,启动Servlet容器).</p>
<p>&nbsp;&nbsp;也可以让自定义的Initializer类继承<strong>AbstractSecurityWebApplicationInitializer</strong>抽象类,这个抽象类实现了WebApplicationInitializer接口,并通过onStartup方法调用.它已经注册了<strong>DelegationFilterProxy</strong>过滤器.</p>
<h4 id="Spring-Security配置类"><a href="#Spring-Security配置类" class="headerlink" title="Spring Security配置类"></a>Spring Security配置类</h4><hr>
<p>&nbsp;&nbsp;只需要在配置类上使用<code>@EnableWebSecurity</code>注解,然后继承<strong>WebSecurityConfigurerAdapter</strong>.可以通过重写configure方法来配置相关的内容.</p>
<p>&nbsp;&nbsp;在Spring Security中,通过重写以下方法放行静态资源:</p>
<p><img src="http://ww1.sinaimg.cn/mw690/63503acbjw1f6lgu4c6xxj20h404x0tn.jpg"></p>
<h4 id="Authentication"><a href="#Authentication" class="headerlink" title="Authentication"></a>Authentication</h4><hr>
<p>&nbsp;&nbsp;在Spring Security中,通过重写以下方法来完成认证的配置:</p>
<p><img src="http://ww3.sinaimg.cn/mw690/63503acbjw1f6lft8zsnrj20mu01i3yx.jpg"></p>
<p>&nbsp;&nbsp;认证用户需要用户数据的来源,AuthenticationManagerBuilder提供了内存中和JDBC的两种用户数据来源.</p>
<ul>
<li><p>内存中</p>
<p><img src="http://ww3.sinaimg.cn/mw690/63503acbjw1f6lft99qxaj20mj05fjsf.jpg"></p>
</li>
<li><p>JDBC</p>
<p><img src="http://ww3.sinaimg.cn/mw690/63503acbjw1f6lft9j25zj20mo09b40j.jpg"></p>
</li>
</ul>
<p>&nbsp;&nbsp;如果需要自定义用户数据来源,则可以通过实现<strong>UserDetailsService</strong>接口.</p>
<p><img src="http://ww2.sinaimg.cn/mw690/63503acbjw1f6lg2d5ezej20gn0cadhp.jpg" alt="Role实体类"></p>
<p><img src="http://ww4.sinaimg.cn/mw690/63503acbjw1f6lg2cv17nj20ls0g80wk.jpg" alt="User实体类"></p>
<p><img src="http://ww3.sinaimg.cn/mw690/63503acbjw1f6lg2dfhjxj20qe0d7tcp.jpg" alt="自定义用户认证"></p>
<p><img src="http://ww2.sinaimg.cn/mw690/63503acbjw1f6lg2dua5xj20md06s75u.jpg" alt="注册自定义的用户认证"></p>
<h4 id="Authorization"><a href="#Authorization" class="headerlink" title="Authorization"></a>Authorization</h4><hr>
<p>&nbsp;&nbsp;在Spring Security中,通过重写以下方法来完成授权的配置:</p>
<p><img src="http://ww4.sinaimg.cn/mw690/63503acbjw1f6lgfcr8g3j20hy01jt8z.jpg"></p>
<p>&nbsp;&nbsp;Spring Security使用2种匹配器用来匹配请求路径:</p>
<ul>
<li>antMatchers:使用Ant风格的路径匹配.</li>
<li>regexMatchers:使用正则表达式匹配路径.</li>
</ul>
<p>&nbsp;&nbsp;Spring Security提供以下方法用于安全处理:</p>
<table>
<thead>
<tr>
<th>Method</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td>anyRequest()</td>
<td>匹配所有请求路径.</td>
</tr>
<tr>
<td>access(String)</td>
<td>Spring EL表达式结果为true时可以访问.</td>
</tr>
<tr>
<td>anonymous()</td>
<td>匿名可访问.</td>
</tr>
<tr>
<td>denyAll()</td>
<td>用户不能访问.</td>
</tr>
<tr>
<td>fullyAuthenticated()</td>
<td>用户完全认证时可访问.</td>
</tr>
<tr>
<td>hasAnyAuthority(String…)</td>
<td>如果用户有参数,则其中任一权限可访问.</td>
</tr>
<tr>
<td>hasAnyRole(String…)</td>
<td>如果用户有参数,则其中任一角色可访问.</td>
</tr>
<tr>
<td>hasAuthority(String)</td>
<td>如果用户有参数,则其权限可访问.</td>
</tr>
<tr>
<td>hasIpAddress(String)</td>
<td>如果用户来自参数中的IP则可访问.</td>
</tr>
<tr>
<td>hasRole(String)</td>
<td>如果用户有参数中的角色可访问.</td>
</tr>
<tr>
<td>permitAll()</td>
<td>用户可任意访问.</td>
</tr>
<tr>
<td>rememberMe()</td>
<td>允许通过remember-me登陆的用户访问.</td>
</tr>
<tr>
<td>authenticated()</td>
<td>用户登录后可访问</td>
</tr>
</tbody></table>
<h3 id="自定义登录实现"><a href="#自定义登录实现" class="headerlink" title="自定义登录实现"></a>自定义登录实现</h3><hr>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">   <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">configure</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">       http.authorizeRequests()</span><br><span class="line">               .anyRequest().authenticated() <span class="comment">// 所有请求需要认证登陆后才能访问.</span></span><br><span class="line">               .and()</span><br><span class="line">               .formLogin() <span class="comment">// 通过formLogin定制登录操作.</span></span><br><span class="line">                   .loginPage(<span class="string">&quot;/login&quot;</span>) <span class="comment">// 定制登录页面的访问地址.</span></span><br><span class="line">                   .defaultSuccessUrl(<span class="string">&quot;/index&quot;</span>) <span class="comment">// 登录成功后转向的页面.</span></span><br><span class="line">                   .failureUrl(<span class="string">&quot;/login?error&quot;</span>) <span class="comment">// 登录失败后转向的页面.</span></span><br><span class="line">                   .permitAll() </span><br><span class="line">               .and()</span><br><span class="line">               .rememberMe() <span class="comment">// 开启cookie存储用户信息.</span></span><br><span class="line">                   .tokenValiditySeconds(<span class="number">1209600</span>) <span class="comment">// 指定cookie的有效时间,单位为秒.</span></span><br><span class="line">                   .key(<span class="string">&quot;userKey&quot;</span>) <span class="comment">// 指定cookie中的私钥.</span></span><br><span class="line">               .and()</span><br><span class="line">               .logout() <span class="comment">// 通过logout()定制注销操作.</span></span><br><span class="line">                   .logoutUrl(<span class="string">&quot;/logout&quot;</span>) <span class="comment">// 指定注销的URL路径.</span></span><br><span class="line">                   .logoutSuccessUrl(<span class="string">&quot;/index&quot;</span>) <span class="comment">// 指定注销成功后转向的页面.</span></span><br><span class="line">               .permitAll();    </span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>
<h3 id="Spring-Boot支持"><a href="#Spring-Boot支持" class="headerlink" title="Spring Boot支持"></a>Spring Boot支持</h3><hr>
<p>&nbsp;&nbsp;Spring Boot主要通过SecurityAutoConfiguration和SecurityProperties来完成自动配置.</p>
<p>&nbsp;&nbsp;SecurityAutoConfiguration导入了SpringBootWebSecurityConfiguration中的配置,我们可以获得如下的自动配置:</p>
<ol>
<li>自动配置了一个内存中的用户,账号为user,密码在程序启动时print在控制台中.</li>
<li>放行了<code>/css/**</code>,<code>/js/**</code>,<code>/images/**</code>,<code>/**/favicon.ico</code>等静态文件存放的路径.</li>
<li>自动配置的securityFilterChainRegistration的Bean.</li>
<li>使用以<code>security</code>为前缀的属性配置Security相关的配置.<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">security.user.name&#x3D;#内存中的默认用户账号,默认为user.</span><br><span class="line">security.user.password&#x3D;#默认用户的密码.</span><br><span class="line">security.user.role&#x3D;#默认用户的角色.</span><br><span class="line">security.require-ssl&#x3D;false #是否需要ssl支持,默认为false.</span><br><span class="line">security.enable-csrf&#x3D;false #是否开启“跨站请求伪造”支持,默认false.</span><br><span class="line">security.ignored&#x3D; #用逗号隔开需要放行的路径.</span><br><span class="line">....</span><br></pre></td></tr></table></figure></li>
</ol>
<p><strong>当我们需要自定义扩展配置的时候,只需要配置类继承WebSecurityConfigurerAdapter即可,不需要使用<code>@EnableWebSecurity</code>注解开启支持.</strong>    </p>
<h3 id="end"><a href="#end" class="headerlink" title="end"></a>end</h3><blockquote>
<p>资料参考于 JavaEE开发的颠覆者: Spring Boot实战</p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2016/08/01/2016-08-1-Spring-data-transaction/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2016/08/01/2016-08-1-Spring-data-transaction/" class="post-title-link" itemprop="url">Spring Data 事务&缓存</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2016-08-01 18:00:00" itemprop="dateCreated datePublished" datetime="2016-08-01T18:00:00+08:00">2016-08-01</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">后端</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/Spring-Boot/" itemprop="url" rel="index"><span itemprop="name">Spring Boot</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="Spring-Transaction"><a href="#Spring-Transaction" class="headerlink" title="Spring Transaction"></a>Spring Transaction</h3><hr>
<p>&nbsp;&nbsp;Spring的事务机制是用统一的机制来处理不同数据访问计数的事务处理.它提供了一个<strong>PlatformTransactionManager</strong>接口,不同的数据访问技术使用不同的接口实现.</p>
<table>
<thead>
<tr>
<th>数据访问技术</th>
<th>接口实现</th>
</tr>
</thead>
<tbody><tr>
<td>JDBC</td>
<td>DataSourceTransactionManager</td>
</tr>
<tr>
<td>JPA</td>
<td>JpaTransactionManager</td>
</tr>
<tr>
<td>Hibernate</td>
<td>HibernateTransactionManager</td>
</tr>
<tr>
<td>JDO</td>
<td>JdoTransactionManager</td>
</tr>
<tr>
<td>分布式事务</td>
<td>JtaTransactionManager</td>
</tr>
</tbody></table>
<h4 id="声明式事务"><a href="#声明式事务" class="headerlink" title="声明式事务"></a>声明式事务</h4><hr>
<p>&nbsp;&nbsp;Spring使用<code>@Transactional</code>注解在方法上表明事务支持.被注解的方法在被调用时,会开启一个新的事务,当方法无异常完成提交后.Spring会提交事务.</p>
<p>&nbsp;&nbsp;<code>@Transactional</code>注解也可以用在类上,表明这个类下的所有方法都有事务支持.如果类和方法都使用了<code>@Transactional</code>,则使用方法上的注解覆盖类级别注解.</p>
<p>&nbsp;&nbsp;<code>@Transactional</code>注解是基于AOP的实现操作.</p>
<p>&nbsp;&nbsp;注意:<code>@Transactional</code>注解是由Spring提供的,而不是来自javax.transaction.</p>
<p>&nbsp;&nbsp;Spring提供<code>@EnableTransactionManagerment</code>注解在配置类上开启声明式事务,Spring容器会自动扫描带有注解<code>@Transactional</code>的类和方法.</p>
<h4 id="Spring-Data-JPA支持"><a href="#Spring-Data-JPA支持" class="headerlink" title="Spring Data JPA支持"></a>Spring Data JPA支持</h4><hr>
<p>&nbsp;&nbsp;Spring Data JPA对所有方法默认开启了事务支持,查询类事务默认启用readOnly-true属性.</p>
<h4 id="Spring-Boot支持"><a href="#Spring-Boot支持" class="headerlink" title="Spring Boot支持"></a>Spring Boot支持</h4><hr>
<p>&nbsp;&nbsp;Spring Boot会自动配置事务管理器.</p>
<ul>
<li>当使用JDBC时,Spring Boot会自动配置DataSourceTransactionManager.</li>
<li>当使用JPA时,Spring Boot会自动配置JpaTransactionManager.</li>
</ul>
<p>&nbsp;&nbsp;在Spring Boot中,不需要显式开启<code>@EnableTransactionManagement</code>注解.</p>
<h3 id="Spring-Cache"><a href="#Spring-Cache" class="headerlink" title="Spring Cache"></a>Spring Cache</h3><hr>
<p>&nbsp;&nbsp;Spring提供了CacheManager和Cache接口用来统一各种不同的数据缓存技术.</p>
<ul>
<li>CacheManager是各种缓存技术抽象接口.</li>
<li>Cache接口包含各种缓存操作.</li>
</ul>
<table>
<thead>
<tr>
<th>CacheManager</th>
<th>描述</th>
</tr>
</thead>
<tbody><tr>
<td>SimpleCacheManager</td>
<td>使用简单的Collection存储缓存,主要用于测试.</td>
</tr>
<tr>
<td>NoOpCacheManager</td>
<td>不会实际存储缓存.</td>
</tr>
<tr>
<td>EhCacheCacheManager</td>
<td>使用EhCache缓存技术.</td>
</tr>
<tr>
<td>ConcurrentMapCacheManager</td>
<td>使用ConcurrentMap存储缓存.</td>
</tr>
<tr>
<td>GuavaCacheManager</td>
<td>使用Google Guava的GuavaCache缓存技术.</td>
</tr>
<tr>
<td>HazelcastCacheManager</td>
<td>使用Hazelcast缓存技术.</td>
</tr>
<tr>
<td>JCacheCacheManager</td>
<td>支持JCache(JSR-107)规范的实现作为缓存技术.</td>
</tr>
<tr>
<td>RedisCacheManager</td>
<td>使用Redis作为缓存技术.</td>
</tr>
</tbody></table>
<p><strong>&nbsp;&nbsp;不管使用什么缓存技术,都需要注册一个该实现的CacheManager的Bean.</strong></p>
<h4 id="声明式缓存"><a href="#声明式缓存" class="headerlink" title="声明式缓存"></a>声明式缓存</h4><hr>
<p>&nbsp;&nbsp;Spring提供了以下注解用来声明式缓存.它与<code>@Transactional</code>注解一样是基于AOP操作的实现.</p>
<table>
<thead>
<tr>
<th>Annotation</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td>@CachePut</td>
<td>不管什么情况,都会把方法的返回值存入缓存中.@CachePut的属性与@Cacheable保持一致.</td>
</tr>
<tr>
<td>@Cacheable</td>
<td>Spring会先查看缓存中是否存有数据,如果有,则直接返回缓存数据,如果没有,则将调用方法的返回值存入缓存中.</td>
</tr>
<tr>
<td>@Caching</td>
<td>可以通过@Caching注解组合多个注解策略在一个方法上.</td>
</tr>
<tr>
<td>@CacheEvict</td>
<td>将一条或多条缓存数据从缓存中删除.</td>
</tr>
</tbody></table>
<p><strong>&nbsp;&nbsp;开启声明式缓存需要在配置类上使用注解<code>@EnableCaching</code></strong></p>
<h4 id="Example"><a href="#Example" class="headerlink" title="Example"></a>Example</h4><hr>
<p><img src="http://ww2.sinaimg.cn/mw690/63503acbjw1f6ldwfwuexj20ol0k5afd.jpg"></p>
<h4 id="Spring-Boot支持-1"><a href="#Spring-Boot支持-1" class="headerlink" title="Spring Boot支持"></a>Spring Boot支持</h4><hr>
<p>&nbsp;&nbsp;Spring Boot自动配置了CacheManager的各种实现,默认情况下使用的是ConcurrentMapCacheManager.支持以<code>spring.cache</code>为前缀的属性来配置缓存相关的配置.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">spring.cache.type&#x3D; #缓存技术的类型,可选ehcache,guava,simple,none,generic,hazelcast,infinispan,jcache,redis.</span><br><span class="line">spring.cache.cache-name&#x3D;#程序启动时创建缓存名称</span><br><span class="line">spring.cache.ehcache.config&#x3D;#ehcache配置文件的地址.</span><br><span class="line">spring.cache.hazelcast.config&#x3D;#hazelcast配置文件的地址.</span><br><span class="line">spring.cache.infinispan.config&#x3D;#infinispan配置文件的地址.</span><br><span class="line">spring.cache.jcache.config&#x3D;#jcache配置文件的地址.</span><br><span class="line">spring.cache.jcache.provider&#x3D;#当多个jcache实现在类路径中的时候,指定jcache实现.</span><br><span class="line">spring.cache.guava.spec&#x3D;# guava specs</span><br></pre></td></tr></table></figure>
<p><strong>&nbsp;&nbsp;使用Spring Boot只需要导入相关缓存技术的依赖,并在配置类使用<code>@EnableCaching</code>注解开启缓存支持即可.</strong></p>
<h3 id="end"><a href="#end" class="headerlink" title="end"></a>end</h3><blockquote>
<p>资料参考于 JavaEE开发的颠覆者: Spring Boot实战</p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2016/07/31/2016-07-31-Spring-data/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2016/07/31/2016-07-31-Spring-data/" class="post-title-link" itemprop="url">Spring数据访问方案-Spring Data</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2016-07-31 18:00:00" itemprop="dateCreated datePublished" datetime="2016-07-31T18:00:00+08:00">2016-07-31</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">后端</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/Spring-Boot/" itemprop="url" rel="index"><span itemprop="name">Spring Boot</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><p>&nbsp;&nbsp;Spring Data是Spring用来解决数据访问的解决方案,它包含了大量关系型数据库以及NoSQL数据库的数据持久层访问解决方案.<br>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |</p>
<h4 id="Spring-Data-Commons"><a href="#Spring-Data-Commons" class="headerlink" title="Spring Data Commons"></a>Spring Data Commons</h4><p>&nbsp;&nbsp;Spring Data提供了统一的API来对各种数据存储技术进行数据访问操作,这是通过Spring Data Commons来实现的,它是所有Spring Data子项目的依赖.</p>
<h4 id="Spring-Data-Repository"><a href="#Spring-Data-Repository" class="headerlink" title="Spring Data Repository"></a>Spring Data Repository</h4><p>&nbsp;&nbsp;Spring Data Repository是数据访问的统一标准,它是抽象的,不同的数据访问技术有不同的Repository,它的顶级接口为Repository接口.</p>
<p><img src="http://ww2.sinaimg.cn/mw690/63503acbjw1f6j3zxk8f5j20i104imxu.jpg"></p>
<p><strong>&nbsp;&nbsp;Repository用一个实体类型与ID类型作为泛型.</strong></p>
<p>&nbsp;&nbsp;Repository的子接口CrudRepository定义了CRUD操作的相关内容:</p>
<p><img src="http://ww3.sinaimg.cn/mw690/63503acbjw1f6j45n3hnxj20q00hs0vq.jpg"></p>
<p>&nbsp;&nbsp;CrudRepository的子接口PagingAndSortingRepository定义了分页与排序的相关内容:</p>
<p><img src="http://ww3.sinaimg.cn/mw690/63503acbjw1f6j45ne4tnj20ti07aabj.jpg"></p>
<h3 id="Spring-Data-JPA"><a href="#Spring-Data-JPA" class="headerlink" title="Spring Data JPA"></a>Spring Data JPA</h3><h4 id="JPA规范"><a href="#JPA规范" class="headerlink" title="JPA规范"></a>JPA规范</h4><hr>
<p>&nbsp;&nbsp;JPA是Java Persistence API的缩写,它是一个基于O/R(Object-Relational Mapping)映射的标准规范.例如Hibernate就是JPA规范的实现.</p>
<h4 id="数据访问层"><a href="#数据访问层" class="headerlink" title="数据访问层"></a>数据访问层</h4><hr>
<ol>
<li>在Spring Data JPA中定义数据访问层首先需要继承JpaRepository接口.</li>
<li>之后可以通过@EnableJpaRepository注解开启Spring Data JPA的支持,@EnableJpaRepository接收的value参数用于扫描数据访问层所在包下的接口定义.</li>
</ol>
<h4 id="查询方法"><a href="#查询方法" class="headerlink" title="查询方法"></a>查询方法</h4><hr>
<p>&nbsp;&nbsp;Spring Data JPA支持通过定义在Repository接口中的方法名来定义查询,方法名是根据实体类的属性名来确定的.</p>
<p><img src="http://ww3.sinaimg.cn/mw690/63503acbjw1f6j94ghv86j20ma0bjtaw.jpg"></p>
<p>&nbsp;&nbsp;其中findBy关键字可以用find、read、readBy、query、queryBy、get、getBy替代.</p>
<p>&nbsp;&nbsp;Spring Data JPA可以使用top和first关键字查询指定数量的数据.</p>
<p><img src="http://ww1.sinaimg.cn/mw690/63503acbjw1f6j9asrv0sj20dq0543yx.jpg"></p>
<h4 id="查询关键字"><a href="#查询关键字" class="headerlink" title="查询关键字"></a>查询关键字</h4><hr>
<table>
<thead>
<tr>
<th>关键字</th>
<th>示例</th>
<th>同功能JPQL</th>
</tr>
</thead>
<tbody><tr>
<td>And</td>
<td>findByNameAndAge</td>
<td>where x.name = ?1 and x.age = ?2</td>
</tr>
<tr>
<td>Or</td>
<td>findByNameOrAge</td>
<td>where x.name = ?1 or x.age = ?2</td>
</tr>
<tr>
<td>Is</td>
<td>findByNameIs</td>
<td>where x.name = ?1</td>
</tr>
<tr>
<td>Equals</td>
<td>findByNameEquals</td>
<td>where x.name = ?1</td>
</tr>
<tr>
<td>Between</td>
<td>findByAgeBetween</td>
<td>where x.age between ?1 and ?2</td>
</tr>
<tr>
<td>LessThan</td>
<td>findByAgeLessThan</td>
<td>where x.age &lt; ?1</td>
</tr>
<tr>
<td>LessThanEqual</td>
<td>findByAgeLessThanEqual</td>
<td>where x.age &lt;= ?1</td>
</tr>
<tr>
<td>GreaterThan</td>
<td>findByAgeGreaterThan</td>
<td>where x.age &gt; ?1</td>
</tr>
<tr>
<td>GreaterThanEqual</td>
<td>findByAgeGreaterThanEqual</td>
<td>where x.age &gt;= ?1</td>
</tr>
<tr>
<td>After</td>
<td>findByStartDateAfter</td>
<td>where x.startDate &gt; ?1</td>
</tr>
<tr>
<td>Before</td>
<td>findByStartDateBefore</td>
<td>where x.startDate &lt; ?1</td>
</tr>
<tr>
<td>IsNull</td>
<td>findByNameIsNull</td>
<td>where x.name is null</td>
</tr>
<tr>
<td>IsNotNull&amp;NotNull</td>
<td>findByName(Is)NotNull</td>
<td>where x.name not null</td>
</tr>
<tr>
<td>Like</td>
<td>findByNameLike</td>
<td>where x.name like ?1</td>
</tr>
<tr>
<td>NotLike</td>
<td>findByNameNotLike</td>
<td>where x.name not like ?1</td>
</tr>
<tr>
<td>StartingWith</td>
<td>findByNameStartingWith</td>
<td>where x.name like ?1(参数前面加%)</td>
</tr>
<tr>
<td>EndingWith</td>
<td>findByNameEndingWith</td>
<td>where x.name like ?1(参数后面加%)</td>
</tr>
<tr>
<td>Containing</td>
<td>findByNameContaining</td>
<td>where x.name like ?1(参数前后都加%)</td>
</tr>
<tr>
<td>OrderBy</td>
<td>findByNameOrderByAgeDesc</td>
<td>where x.name = ?1 order by x.age desc</td>
</tr>
<tr>
<td>Not</td>
<td>findByNameNot</td>
<td>where x.name &lt;&gt; ?1</td>
</tr>
<tr>
<td>In</td>
<td>findByAgeIn(Collection<Age> age)</td>
<td>where x.age in ?1</td>
</tr>
<tr>
<td>NotIn</td>
<td>findByAgeNotIn(Collection<Age> age)</td>
<td>where x.age not in ?1</td>
</tr>
<tr>
<td>True</td>
<td>findByActiveTrue()</td>
<td>where x.active = true</td>
</tr>
<tr>
<td>False</td>
<td>findByActiveFalse()</td>
<td>where x.active = false</td>
</tr>
<tr>
<td>IgnoreCase</td>
<td>findByNameIgnoreCase</td>
<td>where UPPER(x.name) = UPPER(?1)</td>
</tr>
</tbody></table>
<h4 id="Query-amp-NamedQuery查询"><a href="#Query-amp-NamedQuery查询" class="headerlink" title="@Query&amp;@NamedQuery查询"></a>@Query&amp;@NamedQuery查询</h4><hr>
<p>&nbsp;&nbsp;Spring Data JPA支持使用@Query注解在接口的方法上实现查询.</p>
<p><img src="http://ww1.sinaimg.cn/mw690/63503acbjw1f6jatc82fnj20rf06375q.jpg"></p>
<p>&nbsp;&nbsp;Spring Data JPA支持使用@NamedQuery定义查询方法,一个名称映射一条查询语句.</p>
<p><img src="http://ww2.sinaimg.cn/mw690/63503acbjw1f6jatcdurzj20kq02uaar.jpg"></p>
<p><img src="http://ww2.sinaimg.cn/mw690/63503acbjw1f6jatcnebfj20jn05k0tl.jpg"></p>
<h4 id="更新查询"><a href="#更新查询" class="headerlink" title="更新查询"></a>更新查询</h4><hr>
<p>&nbsp;&nbsp;Spring Data JPA支持使用@Modifying注解和@Query注解组合进行数据更新操作.</p>
<p><img src="http://ww4.sinaimg.cn/mw690/63503acbjw1f6jthq9ojpj20do05fgml.jpg"></p>
<h4 id="Specification"><a href="#Specification" class="headerlink" title="Specification"></a>Specification</h4><hr>
<p>&nbsp;&nbsp;Spring Data JPA提供了一个Specification接口可以让我们快速地构造基于准则的查询.通过重写Specification接口的toPredicate方法用来构造查询条件.</p>
<ol>
<li><p>数据访问接口需继承JpaSpecificationExecutor接口.</p>
</li>
<li><p>构造查询条件类</p>
<p><img src="http://ww1.sinaimg.cn/mw690/63503acbjw1f6judons88j20pz0cugoi.jpg"></p>
</li>
<li><p>调用条件查询</p>
<p><img src="http://ww2.sinaimg.cn/mw690/63503acbjw1f6judoyj61j20go031q3d.jpg"></p>
</li>
</ol>
<h4 id="排序-amp-分页"><a href="#排序-amp-分页" class="headerlink" title="排序&amp;分页"></a>排序&amp;分页</h4><hr>
<p>&nbsp;&nbsp;Spring Data JPA提供了Sort类和Page接口及Pageable接口完成排序和分页.</p>
<p><img src="http://ww4.sinaimg.cn/mw690/63503acbjw1f6julp4p8mj20ph0c40v9.jpg"></p>
<h4 id="自定义Repository"><a href="#自定义Repository" class="headerlink" title="自定义Repository"></a>自定义Repository</h4><hr>
<p>&nbsp;&nbsp;如果我们想自定义Repository,可以继承Repository的子接口PagingAndSortingRepository.</p>
<ul>
<li><p>定义自定义的Repository接口.</p>
<p><img src="http://ww2.sinaimg.cn/mw690/63503acbjw1f6jv8nwlvej20j006n0tw.jpg"></p>
</li>
<li><p>定义自定义的Repository接口实现.</p>
<p><img src="http://ww3.sinaimg.cn/mw690/63503acbjw1f6jv8o9wlrj20qp0dyn0m.jpg"></p>
</li>
<li><p>定义RepositoryFactoryBean</p>
<p><img src="http://ww2.sinaimg.cn/mw690/63503acbjw1f6jv8oxqiwj20t00nxgsx.jpg"></p>
</li>
<li><p>开启自定义支持需要使用@EnableJpaRepositories注解的repositoryFactoryBeanClass指定FactoryBean.</p>
</li>
</ul>
<h4 id="Spring-Boot支持"><a href="#Spring-Boot支持" class="headerlink" title="Spring Boot支持"></a>Spring Boot支持</h4><hr>
<ul>
<li><p>Spring Boot使用<code>spring.datasource</code>前缀用来配置dataSource.</p>
</li>
<li><p>Spring Boot自动开启了注解事务支持(@EnableTransactionManagement),并配置了jdbcTemplate.</p>
</li>
<li><p>Spring Boot提供了初始化数据的功能,在类路径下的schema.sql文件会自动初始化表结构;在类路径下的data.sql文件会自动插入表数据.</p>
</li>
<li><p>Spring Boot为我们自动配置了transactionManager、jpaVendorAdapter、entityManagerFactory等Bean.JpaBaseConfiguration还有一个getPackagesToScan方法用于自动扫描带有注解@Entity的实体类</p>
</li>
<li><p>Spring Boot自动配置了OpenEntityManagerInViewInterceptor拦截器,并注册到了Spring MVC的拦截器中.解决了页面访问数据时会话连接已关闭的错误.</p>
</li>
<li><p>Spring Boot自动开启了对Spring Data JPA的支持,无需再配置类中显式声明@EnableJpaRepositories.</p>
</li>
</ul>
<p>&nbsp;&nbsp;在Spring Boot下使用Spring Data JPA,只需要添加依赖spring-boot-stater-data-jpa,然后定义DataSource、实体类、数据访问层即可,无需其他配置.</p>
<h3 id="Spring-Data-REST"><a href="#Spring-Data-REST" class="headerlink" title="Spring Data REST"></a>Spring Data REST</h3><hr>
<p>&nbsp;&nbsp;Spring Data REST支持将Spring Data JPA,Spring Data MongoDB,Spring Data Neo4j,Spring Data GernFile,Spring Data Cassandra的Repository自动转换成REST服务.</p>
<p>&nbsp;&nbsp;Spring Data REST的配置是定义在<strong>RepositoryRestMvcConfiguration</strong>配置类中的,我们可以通过继承这个配置类或者使用@Import注解导入此配置类来使用Spring Data REST.</p>
<h4 id="Spring-Boot支持-1"><a href="#Spring-Boot支持-1" class="headerlink" title="Spring Boot支持"></a>Spring Boot支持</h4><hr>
<p>&nbsp;&nbsp;Spring Boot已经自动配置了<strong>RepositoryRestMvcConfiguration</strong>,所以只需要引入依赖spring-boot-starter-data-rest,不需要任何其他配置.</p>
<ul>
<li><p>Spring Boot使用<code>spring.data.rest</code>前缀用来配置<strong>RepositoryRestMvcConfiguration</strong>的属性.</p>
</li>
<li><p>如果想在自定义的领域类Repository中将方法暴露为REST资源,则需要使用@RestResource注解.<br><img src="http://ww1.sinaimg.cn/mw690/63503acbjw1f6l1pub41ej20hw01ydga.jpg" alt="http://localhost:8080/persons/search/nameStartsWith?name=xx访问此REST资源."></p>
</li>
<li><p>如需要分页,则可以使用参数page=?&amp;size=?来实现分页.<br>例:<a target="_blank" rel="noopener" href="http://localhost:8080/persons/?page=1&amp;size=10">http://localhost:8080/persons/?page=1&amp;size=10</a>.</p>
</li>
<li><p>如需要排序,则可以使用参数sort来实现排序.<br>例:<a target="_blank" rel="noopener" href="http://localhost:8080/persons/?sort=age,desc">http://localhost:8080/persons/?sort=age,desc</a>.</p>
</li>
<li><p>自定义根路径需要在application.properties中设置<code>spring.data.rest.base-path</code>属性.</p>
</li>
<li><p>Spring Data REST的节点路径是默认在实体类之后加s,如果需要自定义节点路径则要在领域类Repository上使用**@RepositoryRestResource**注解的path属性进行设置.</p>
</li>
</ul>
<h3 id="Spring-Data-MongoDB"><a href="#Spring-Data-MongoDB" class="headerlink" title="Spring Data MongoDB"></a>Spring Data MongoDB</h3><hr>
<p>&nbsp;&nbsp;MongoDB是一个基于Document文档的NoSQL数据库,它使用面向对象的思想,每一条记录都是一个文档对象.</p>
<p>&nbsp;&nbsp;Spring Data MongoDB提供了以下的注解用来定义领域类:</p>
<table>
<thead>
<tr>
<th>注解</th>
<th>描述</th>
</tr>
</thead>
<tbody><tr>
<td>@Document</td>
<td>映射领域对象与MongoDB的一个文档</td>
</tr>
<tr>
<td>@Id</td>
<td>映射当前属性为ID</td>
</tr>
<tr>
<td>@DbRef</td>
<td>当前属性将参考其他文档</td>
</tr>
<tr>
<td>@Field</td>
<td>为文档的属性定义名称</td>
</tr>
<tr>
<td>@Version</td>
<td>将当前属性作为版本</td>
</tr>
</tbody></table>
<p>&nbsp;&nbsp;Spring Data MongoDB还提供了一个MongoTemplate封装了数据访问的方法,我们还需要为MongoClient和MongoDbFactory来配置数据库连接属性.开启MongoDB的Repository需要在配置类上使用注解@EnableMongoRepositories.</p>
<p><img src="http://ww4.sinaimg.cn/mw690/63503acbjw1f6l41qx7pbj20qa0am775.jpg"></p>
<p>&nbsp;&nbsp;定义Spring Data MongoDB的Repository只需要继承MongoRepository接口即可.</p>
<h4 id="Spring-Boot支持-2"><a href="#Spring-Boot支持-2" class="headerlink" title="Spring Boot支持"></a>Spring Boot支持</h4><hr>
<p>&nbsp;&nbsp;使用Spirng Boot主要配置数据库连接、MongoTemplate.可以使用<code>spring.data.mongodb</code>配置MongoDB相关的属性.</p>
<ul>
<li>Spring Boot自动开启了@EnableMongoRepositories注解.</li>
<li>Spring Boot提供了一些默认配置:默认MongoDB端口为27017,服务器为localhost,数据库为test.</li>
<li>在Spring Boot下使用MongoDB只需要引入依赖spring-boot-starter-data-mongodb,不需要其他配置.</li>
</ul>
<h3 id="Spring-Data-Redis"><a href="#Spring-Data-Redis" class="headerlink" title="Spring Data Redis"></a>Spring Data Redis</h3><hr>
<p>&nbsp;&nbsp;Spring Data Redis提供了ConnectionFactory和RedisTemplate.</p>
<ul>
<li><p>根据Redis不同的JavaClient,Spring Data Redis提供了不同的ConnectionFactory.</p>
<ul>
<li>JedisConnectionFactory:使用Jedis作为客户端.</li>
<li>JredisConnectionFactory:使用Jredis作为客户端.</li>
<li>LettuceConnectionFactory:使用Lettuce作为客户端.</li>
<li>SrpConnectionFactory:使用Spullara/redis-protoccol作为客户端.</li>
</ul>
</li>
<li><p>配置ConnectionFactory和RedisTemplate如下:<br><img src="http://ww1.sinaimg.cn/mw690/63503acbjw1f6l51jk3k8j20m707iwgh.jpg"></p>
</li>
</ul>
<p>&nbsp;&nbsp;Spring Data Redis提供了RedisTemplate和StringRedisTemplate两个模板对象进行数据操作.StringRedisTemplate只针对键值都是字符类型的数据进行操作.</p>
<table>
<thead>
<tr>
<th>数据操作方法</th>
<th>描述</th>
</tr>
</thead>
<tbody><tr>
<td>opsForValue()</td>
<td>操作简单属性的数据</td>
</tr>
<tr>
<td>opsForList()</td>
<td>操作list数据</td>
</tr>
<tr>
<td>opsForSet()</td>
<td>操作set数据</td>
</tr>
<tr>
<td>opsForZSet()</td>
<td>操作ZSet(有序的set)数据</td>
</tr>
<tr>
<td>opsForHash()</td>
<td>操作hash散列的数据</td>
</tr>
</tbody></table>
<h4 id="Serializer"><a href="#Serializer" class="headerlink" title="Serializer"></a>Serializer</h4><hr>
<p>&nbsp;&nbsp;当我们进行存储操作的时候,键值对都是通过Spring提供的Serializer序列化到数据库的.</p>
<ul>
<li>RedisTemplate默认使用的是JdkSerializationRedisSerizlizer.</li>
<li>StringRedisTemplate默认使用的是StringRedisSerializer.</li>
</ul>
<h4 id="Spring-Boot支持-3"><a href="#Spring-Boot支持-3" class="headerlink" title="Spring Boot支持"></a>Spring Boot支持</h4><hr>
<p>&nbsp;&nbsp;Spring Boot默认配置了JedisConnectionFactory、RedisTemplate和StringRedisTemplate.</p>
<p>&nbsp;&nbsp;Spring Boot使用<code>spring.redis</code>为前缀在application.properties中配置Redis相关的属性.</p>
<p><img src="http://ww2.sinaimg.cn/mw690/63503acbjw1f6l5oypp5fj20j90fxwhx.jpg"></p>
<h3 id="end"><a href="#end" class="headerlink" title="end"></a>end</h3><blockquote>
<p>资料参考于 JavaEE开发的颠覆者: Spring Boot实战</p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/yuwanzi.io/page/4/"><i class="fa fa-angle-left" aria-label="Vorherige Seite"></i></a><a class="page-number" href="/yuwanzi.io/">1</a><span class="space">&hellip;</span><a class="page-number" href="/yuwanzi.io/page/4/">4</a><span class="page-number current">5</span><a class="page-number" href="/yuwanzi.io/page/6/">6</a><a class="page-number" href="/yuwanzi.io/page/7/">7</a><a class="extend next" rel="next" href="/yuwanzi.io/page/6/"><i class="fa fa-angle-right" aria-label="Nächste Seite"></i></a>
  </nav>


<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      const activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      const commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>
</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">玉丸子</span>
</div>
  <div class="powered-by">Erstellt mit  <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" class="theme-link" rel="noopener" target="_blank">NexT.Muse</a>
  </div>

    </div>
  </footer>

  
  <script src="//cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js"></script>
<script src="/yuwanzi.io/js/utils.js"></script><script src="/yuwanzi.io/js/motion.js"></script><script src="/yuwanzi.io/js/schemes/muse.js"></script><script src="/yuwanzi.io/js/next-boot.js"></script>

  






  





</body>
</html>
